diff options
| author | Samuel Tardieu <sam@rfc1149.net> | 2011-09-14 23:54:46 +0200 |
|---|---|---|
| committer | Samuel Tardieu <sam@rfc1149.net> | 2011-09-14 23:54:46 +0200 |
| commit | ccaab5d0b312b28ab15833ab8f11dd809ec93aab (patch) | |
| tree | 4f80418eeb724a0c15ec6c80621e647b63a1a826 | |
| parent | 4f7c67407019de18fd53b640edb9682a346fbfef (diff) | |
| download | cgeo-ccaab5d0b312b28ab15833ab8f11dd809ec93aab.zip cgeo-ccaab5d0b312b28ab15833ab8f11dd809ec93aab.tar.gz cgeo-ccaab5d0b312b28ab15833ab8f11dd809ec93aab.tar.bz2 | |
Convert to Unix end-of-line format
53 files changed, 30336 insertions, 30336 deletions
diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs index c5abbb1..7df68fd 100644 --- a/.settings/org.eclipse.core.runtime.prefs +++ b/.settings/org.eclipse.core.runtime.prefs @@ -1,3 +1,3 @@ -#Wed Sep 14 19:24:20 CEST 2011
-eclipse.preferences.version=1
-line.separator=\n
+#Wed Sep 14 19:24:20 CEST 2011 +eclipse.preferences.version=1 +line.separator=\n diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 75e8cdb..aaf1455 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,284 +1,284 @@ -#Wed Sep 14 21:12:02 CEST 2011
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-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=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0
-org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0
-org.eclipse.jdt.core.formatter.alignment_for_assignment=0
-org.eclipse.jdt.core.formatter.alignment_for_binary_expression=0
-org.eclipse.jdt.core.formatter.alignment_for_compact_if=0
-org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0
-org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0
-org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=0
-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=0
-org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
-org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
-org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
-org.eclipse.jdt.core.formatter.blank_lines_after_package=1
-org.eclipse.jdt.core.formatter.blank_lines_before_field=0
-org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
-org.eclipse.jdt.core.formatter.blank_lines_before_imports=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=0
-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=true
-org.eclipse.jdt.core.formatter.comment.format_html=true
-org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true
-org.eclipse.jdt.core.formatter.comment.format_line_comments=false
-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=120
-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.comment.preserve_white_space_between_code_and_line_comments=false
-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=true
-org.eclipse.jdt.core.formatter.indentation.size=4
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
-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_method=insert
-org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=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_annotation_on_type=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=do not 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=false
-org.eclipse.jdt.core.formatter.join_wrapped_lines=false
-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=false
-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=false
-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
+#Wed Sep 14 21:12:02 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +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=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=0 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=0 +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=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=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=0 +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=true +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=false +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=120 +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.comment.preserve_white_space_between_code_and_line_comments=false +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=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +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_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=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_annotation_on_type=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=do not 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=false +org.eclipse.jdt.core.formatter.join_wrapped_lines=false +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=false +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=false +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/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs index 756bd1c..17745dc 100644 --- a/.settings/org.eclipse.jdt.ui.prefs +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -1,61 +1,61 @@ -#Wed Sep 14 20:41:07 CEST 2011
-eclipse.preferences.version=1
-editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
-formatter_profile=_cgeo
-formatter_settings_version=12
-org.eclipse.jdt.ui.ignorelowercasenames=true
-org.eclipse.jdt.ui.importorder=cgeo;com;org;android;java;javax;
-org.eclipse.jdt.ui.ondemandthreshold=99
-org.eclipse.jdt.ui.staticondemandthreshold=99
-sp_cleanup.add_default_serial_version_id=true
-sp_cleanup.add_generated_serial_version_id=false
-sp_cleanup.add_missing_annotations=true
-sp_cleanup.add_missing_deprecated_annotations=true
-sp_cleanup.add_missing_methods=false
-sp_cleanup.add_missing_nls_tags=false
-sp_cleanup.add_missing_override_annotations=true
-sp_cleanup.add_missing_override_annotations_interface_methods=true
-sp_cleanup.add_serial_version_id=false
-sp_cleanup.always_use_blocks=true
-sp_cleanup.always_use_parentheses_in_expressions=false
-sp_cleanup.always_use_this_for_non_static_field_access=false
-sp_cleanup.always_use_this_for_non_static_method_access=false
-sp_cleanup.convert_to_enhanced_for_loop=false
-sp_cleanup.correct_indentation=false
-sp_cleanup.format_source_code=true
-sp_cleanup.format_source_code_changes_only=true
-sp_cleanup.make_local_variable_final=false
-sp_cleanup.make_parameters_final=false
-sp_cleanup.make_private_fields_final=true
-sp_cleanup.make_type_abstract_if_missing_method=false
-sp_cleanup.make_variable_declarations_final=true
-sp_cleanup.never_use_blocks=false
-sp_cleanup.never_use_parentheses_in_expressions=true
-sp_cleanup.on_save_use_additional_actions=false
-sp_cleanup.organize_imports=true
-sp_cleanup.qualify_static_field_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
-sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
-sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
-sp_cleanup.remove_private_constructors=true
-sp_cleanup.remove_trailing_whitespaces=false
-sp_cleanup.remove_trailing_whitespaces_all=true
-sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
-sp_cleanup.remove_unnecessary_casts=true
-sp_cleanup.remove_unnecessary_nls_tags=false
-sp_cleanup.remove_unused_imports=false
-sp_cleanup.remove_unused_local_variables=false
-sp_cleanup.remove_unused_private_fields=true
-sp_cleanup.remove_unused_private_members=false
-sp_cleanup.remove_unused_private_methods=true
-sp_cleanup.remove_unused_private_types=true
-sp_cleanup.sort_members=false
-sp_cleanup.sort_members_all=false
-sp_cleanup.use_blocks=true
-sp_cleanup.use_blocks_only_for_return_and_throw=false
-sp_cleanup.use_parentheses_in_expressions=false
-sp_cleanup.use_this_for_non_static_field_access=false
-sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
-sp_cleanup.use_this_for_non_static_method_access=false
-sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
+#Wed Sep 14 20:41:07 CEST 2011 +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_cgeo +formatter_settings_version=12 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=cgeo;com;org;android;java;javax; +org.eclipse.jdt.ui.ondemandthreshold=99 +org.eclipse.jdt.ui.staticondemandthreshold=99 +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=true +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=true +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=false +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=false +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=false +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=false +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=false +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/build.properties b/build.properties index c1ba646..88fbba1 100644 --- a/build.properties +++ b/build.properties @@ -1,4 +1,4 @@ -
-# Set the output directory for class files to the same directory Eclipse uses. Was bin/classes
-out.classes.dir=bin
+ +# Set the output directory for class files to the same directory Eclipse uses. Was bin/classes +out.classes.dir=bin
\ No newline at end of file diff --git a/src/cgeo/geocaching/ICache.java b/src/cgeo/geocaching/ICache.java index 1560700..9126268 100644 --- a/src/cgeo/geocaching/ICache.java +++ b/src/cgeo/geocaching/ICache.java @@ -1,99 +1,99 @@ -/**
- *
- */
-package cgeo.geocaching;
-
-/**
- * Basic interface for caches
- *
- * @author blafoo
- *
- */
-public interface ICache {
-
- /**
- * @return Geocode like GCxxxx
- */
- public String getGeocode();
-
- /**
- * @return Tradi, multi etc.
- */
- public String getType();
-
- /**
- * @return Displayed owner, might differ from the real owner
- */
- public String getOwner();
-
- /**
- * @return GC username of the owner
- */
- public String getOwnerReal();
-
- /**
- * @return Micro, small etc.
- */
- public String getSize();
-
- /**
- * @return Difficulty assessment
- */
- public Float getDifficulty();
-
- /**
- * @return Terrain assessment
- */
- public Float getTerrain();
-
- /**
- * @return Latitude, e.g. N 52° 12.345
- */
- public String getLatitude();
-
- /**
- * @return Longitude, e.g. E 9° 34.567
- */
- public String getLongitude();
-
- /**
- * @return true if the cache is disabled, false else
- */
- public boolean isDisabled();
-
- /**
- * @return true if the user is the owner of the cache, false else
- */
- public boolean isOwn();
-
- /**
- * @return true is the cache is archived, false else
- */
- public boolean isArchived();
-
- /**
- * @return true is the cache is a Premium Member cache only, false else
- */
- public boolean isMembersOnly();
-
- /**
- * @return Decrypted hint
- */
- public String getHint();
-
- /**
- * @return Description
- */
- public String getDescription();
-
- /**
- * @return Short Description
- */
- public String getShortDescription();
-
- /**
- * @return Name
- */
- public String getName();
-
-}
+/** + * + */ +package cgeo.geocaching; + +/** + * Basic interface for caches + * + * @author blafoo + * + */ +public interface ICache { + + /** + * @return Geocode like GCxxxx + */ + public String getGeocode(); + + /** + * @return Tradi, multi etc. + */ + public String getType(); + + /** + * @return Displayed owner, might differ from the real owner + */ + public String getOwner(); + + /** + * @return GC username of the owner + */ + public String getOwnerReal(); + + /** + * @return Micro, small etc. + */ + public String getSize(); + + /** + * @return Difficulty assessment + */ + public Float getDifficulty(); + + /** + * @return Terrain assessment + */ + public Float getTerrain(); + + /** + * @return Latitude, e.g. N 52° 12.345 + */ + public String getLatitude(); + + /** + * @return Longitude, e.g. E 9° 34.567 + */ + public String getLongitude(); + + /** + * @return true if the cache is disabled, false else + */ + public boolean isDisabled(); + + /** + * @return true if the user is the owner of the cache, false else + */ + public boolean isOwn(); + + /** + * @return true is the cache is archived, false else + */ + public boolean isArchived(); + + /** + * @return true is the cache is a Premium Member cache only, false else + */ + public boolean isMembersOnly(); + + /** + * @return Decrypted hint + */ + public String getHint(); + + /** + * @return Description + */ + public String getDescription(); + + /** + * @return Short Description + */ + public String getShortDescription(); + + /** + * @return Name + */ + public String getName(); + +} diff --git a/src/cgeo/geocaching/LogTemplateProvider.java b/src/cgeo/geocaching/LogTemplateProvider.java index a02f585..bcb5d5c 100644 --- a/src/cgeo/geocaching/LogTemplateProvider.java +++ b/src/cgeo/geocaching/LogTemplateProvider.java @@ -1,155 +1,155 @@ -package cgeo.geocaching;
-
-import org.apache.commons.lang3.StringUtils;
-
-import android.util.Log;
-
-import java.util.HashMap;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * provides all the available templates for logging
- *
- */
-public class LogTemplateProvider {
- public static abstract class LogTemplate {
- private String template;
- private int resourceId;
-
- protected LogTemplate(String template, int resourceId) {
- this.template = template;
- this.resourceId = resourceId;
- }
-
- abstract String getValue(cgBase base, boolean offline);
-
- public int getResourceId() {
- return resourceId;
- }
-
- public int getItemId() {
- return template.hashCode();
- }
-
- public String getTemplateString() {
- return template;
- }
-
- protected String apply(String input, cgBase base, boolean offline) {
- if (input.contains("[" + template + "]")) {
- return input.replaceAll("\\[" + template + "\\]", getValue(base, offline));
- }
- return input;
- }
- }
-
- private static LogTemplate[] templates;
-
- public static LogTemplate[] getTemplates() {
- if (templates == null) {
- templates = new LogTemplate[] {
- new LogTemplate("DATE", R.string.init_signature_template_date) {
-
- @Override
- String getValue(final cgBase base, final boolean offline) {
- return base.formatFullDate(System.currentTimeMillis());
- }
- },
- new LogTemplate("TIME", R.string.init_signature_template_time) {
-
- @Override
- String getValue(final cgBase base, final boolean offline) {
- return base.formatTime(System.currentTimeMillis());
- }
- },
- new LogTemplate("DATETIME", R.string.init_signature_template_datetime) {
-
- @Override
- String getValue(final cgBase base, final boolean offline) {
- final long currentTime = System.currentTimeMillis();
- return base.formatFullDate(currentTime) + " " + base.formatTime(currentTime);
- }
- },
- new LogTemplate("USER", R.string.init_signature_template_user) {
-
- @Override
- String getValue(final cgBase base, final boolean offline) {
- return base.getUserName();
- }
- },
- new LogTemplate("NUMBER", R.string.init_signature_template_number) {
-
- @Override
- String getValue(final cgBase base, final boolean offline) {
- if (offline) {
- return "";
- }
- String findCount = "";
- final Map<String, String> params = new HashMap<String, String>();
- final String page = base.request(false, "www.geocaching.com", "/email/", "GET", params, false, false, false).getData();
- int current = parseFindCount(page);
-
- if (current >= 0) {
- findCount = String.valueOf(current + 1);
- }
- return findCount;
- }
- }
- };
- }
- return templates;
- }
-
- public static LogTemplate getTemplate(int itemId) {
- for (LogTemplate template : getTemplates()) {
- if (template.getItemId() == itemId) {
- return template;
- }
- }
- return null;
- }
-
- public static String applyTemplates(String signature, cgBase base, boolean offline) {
- if (signature == null) {
- return "";
- }
- String result = signature;
- for (LogTemplate template : getTemplates()) {
- result = template.apply(result, base, offline);
- }
- return result;
- }
-
- private static int parseFindCount(String page) {
- if (StringUtils.isBlank(page)) {
- return -1;
- }
-
- int findCount = -1;
-
- try {
- final Pattern findPattern = Pattern.compile("<strong><img.+?icon_smile.+?title=\"Caches Found\" /> ([,\\d]+)", Pattern.CASE_INSENSITIVE);
- final Matcher findMatcher = findPattern.matcher(page);
- if (findMatcher.find()) {
- if (findMatcher.groupCount() > 0) {
- String count = findMatcher.group(1);
-
- if (count != null) {
- if (count.length() == 0) {
- findCount = 0;
- } else {
- findCount = Integer.parseInt(count.replaceAll(",", ""));
- }
- }
- }
- }
- } catch (Exception e) {
- Log.w(cgSettings.tag, "cgBase.parseFindCount: " + e.toString());
- }
-
- return findCount;
- }
-
-}
+package cgeo.geocaching; + +import org.apache.commons.lang3.StringUtils; + +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * provides all the available templates for logging + * + */ +public class LogTemplateProvider { + public static abstract class LogTemplate { + private String template; + private int resourceId; + + protected LogTemplate(String template, int resourceId) { + this.template = template; + this.resourceId = resourceId; + } + + abstract String getValue(cgBase base, boolean offline); + + public int getResourceId() { + return resourceId; + } + + public int getItemId() { + return template.hashCode(); + } + + public String getTemplateString() { + return template; + } + + protected String apply(String input, cgBase base, boolean offline) { + if (input.contains("[" + template + "]")) { + return input.replaceAll("\\[" + template + "\\]", getValue(base, offline)); + } + return input; + } + } + + private static LogTemplate[] templates; + + public static LogTemplate[] getTemplates() { + if (templates == null) { + templates = new LogTemplate[] { + new LogTemplate("DATE", R.string.init_signature_template_date) { + + @Override + String getValue(final cgBase base, final boolean offline) { + return base.formatFullDate(System.currentTimeMillis()); + } + }, + new LogTemplate("TIME", R.string.init_signature_template_time) { + + @Override + String getValue(final cgBase base, final boolean offline) { + return base.formatTime(System.currentTimeMillis()); + } + }, + new LogTemplate("DATETIME", R.string.init_signature_template_datetime) { + + @Override + String getValue(final cgBase base, final boolean offline) { + final long currentTime = System.currentTimeMillis(); + return base.formatFullDate(currentTime) + " " + base.formatTime(currentTime); + } + }, + new LogTemplate("USER", R.string.init_signature_template_user) { + + @Override + String getValue(final cgBase base, final boolean offline) { + return base.getUserName(); + } + }, + new LogTemplate("NUMBER", R.string.init_signature_template_number) { + + @Override + String getValue(final cgBase base, final boolean offline) { + if (offline) { + return ""; + } + String findCount = ""; + final Map<String, String> params = new HashMap<String, String>(); + final String page = base.request(false, "www.geocaching.com", "/email/", "GET", params, false, false, false).getData(); + int current = parseFindCount(page); + + if (current >= 0) { + findCount = String.valueOf(current + 1); + } + return findCount; + } + } + }; + } + return templates; + } + + public static LogTemplate getTemplate(int itemId) { + for (LogTemplate template : getTemplates()) { + if (template.getItemId() == itemId) { + return template; + } + } + return null; + } + + public static String applyTemplates(String signature, cgBase base, boolean offline) { + if (signature == null) { + return ""; + } + String result = signature; + for (LogTemplate template : getTemplates()) { + result = template.apply(result, base, offline); + } + return result; + } + + private static int parseFindCount(String page) { + if (StringUtils.isBlank(page)) { + return -1; + } + + int findCount = -1; + + try { + final Pattern findPattern = Pattern.compile("<strong><img.+?icon_smile.+?title=\"Caches Found\" /> ([,\\d]+)", Pattern.CASE_INSENSITIVE); + final Matcher findMatcher = findPattern.matcher(page); + if (findMatcher.find()) { + if (findMatcher.groupCount() > 0) { + String count = findMatcher.group(1); + + if (count != null) { + if (count.length() == 0) { + findCount = 0; + } else { + findCount = Integer.parseInt(count.replaceAll(",", "")); + } + } + } + } + } catch (Exception e) { + Log.w(cgSettings.tag, "cgBase.parseFindCount: " + e.toString()); + } + + return findCount; + } + +} diff --git a/src/cgeo/geocaching/apps/LocusDataStorageProvider.java b/src/cgeo/geocaching/apps/LocusDataStorageProvider.java index 9fc794f..fcc4ba2 100644 --- a/src/cgeo/geocaching/apps/LocusDataStorageProvider.java +++ b/src/cgeo/geocaching/apps/LocusDataStorageProvider.java @@ -1,70 +1,70 @@ -package cgeo.geocaching.apps;
-
-import menion.android.locus.addon.publiclib.geoData.PointsData;
-import menion.android.locus.addon.publiclib.utils.DataCursor;
-import menion.android.locus.addon.publiclib.utils.DataStorage;
-
-import android.content.ContentProvider;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Parcel;
-
-import java.util.ArrayList;
-
-/**
- * code provided by menion - developer of Locus
- */
-public class LocusDataStorageProvider extends ContentProvider {
-
- @Override
- public Cursor query(Uri aUri, String[] aProjection, String aSelection,
- String[] aSelectionArgs, String aSortOrder) {
-
- DataCursor cursor = new DataCursor(new String[] { "data" });
-
- ArrayList<PointsData> data = DataStorage.getData();
- if (data == null || data.size() == 0)
- return cursor;
-
- for (int i = 0; i < data.size(); i++) {
- // get byte array
- Parcel par = Parcel.obtain();
- data.get(i).writeToParcel(par, 0);
- byte[] byteData = par.marshall();
- // add to row
- cursor.addRow(new Object[] { byteData });
- }
- // data filled to cursor, clear reference to prevent some memory issue
- DataStorage.clearData();
- // now finally return filled cursor
- return cursor;
- }
-
- @Override
- public int delete(Uri uri, String selection, String[] selectionArgs) {
- return 0;
- }
-
- @Override
- public String getType(Uri uri) {
- return null;
- }
-
- @Override
- public Uri insert(Uri uri, ContentValues values) {
- return null;
- }
-
- @Override
- public boolean onCreate() {
- return false;
- }
-
- @Override
- public int update(Uri uri, ContentValues values, String selection,
- String[] selectionArgs) {
- return 0;
- }
-
-}
+package cgeo.geocaching.apps; + +import menion.android.locus.addon.publiclib.geoData.PointsData; +import menion.android.locus.addon.publiclib.utils.DataCursor; +import menion.android.locus.addon.publiclib.utils.DataStorage; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; +import android.os.Parcel; + +import java.util.ArrayList; + +/** + * code provided by menion - developer of Locus + */ +public class LocusDataStorageProvider extends ContentProvider { + + @Override + public Cursor query(Uri aUri, String[] aProjection, String aSelection, + String[] aSelectionArgs, String aSortOrder) { + + DataCursor cursor = new DataCursor(new String[] { "data" }); + + ArrayList<PointsData> data = DataStorage.getData(); + if (data == null || data.size() == 0) + return cursor; + + for (int i = 0; i < data.size(); i++) { + // get byte array + Parcel par = Parcel.obtain(); + data.get(i).writeToParcel(par, 0); + byte[] byteData = par.marshall(); + // add to row + cursor.addRow(new Object[] { byteData }); + } + // data filled to cursor, clear reference to prevent some memory issue + DataStorage.clearData(); + // now finally return filled cursor + return cursor; + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public boolean onCreate() { + return false; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, + String[] selectionArgs) { + return 0; + } + +} diff --git a/src/cgeo/geocaching/cgCoord.java b/src/cgeo/geocaching/cgCoord.java index 09c9d91..17520ca 100644 --- a/src/cgeo/geocaching/cgCoord.java +++ b/src/cgeo/geocaching/cgCoord.java @@ -1,45 +1,45 @@ -package cgeo.geocaching;
-
-import cgeo.geocaching.geopoint.Geopoint;
-
-public class cgCoord {
-
- public Integer id = null;
- public String geocode = "";
- public String type = "cache";
- public String typeSpec = "traditional";
- public String name = "";
- public boolean found = false;
- public boolean disabled = false;
- public Geopoint coords = new Geopoint(0, 0);
- public Float difficulty = null;
- public Float terrain = null;
- public String size = null;
-
- public cgCoord() {
- }
-
- public cgCoord(cgCache cache) {
- disabled = cache.disabled;
- found = cache.found;
- geocode = cache.geocode;
- coords = cache.coords;
- name = cache.name;
- type = "cache";
- typeSpec = cache.type;
- difficulty = cache.difficulty;
- terrain = cache.terrain;
- size = cache.size;
- }
-
- public cgCoord(cgWaypoint waypoint) {
- id = waypoint.id;
- disabled = false;
- found = false;
- geocode = "";
- coords = waypoint.coords;
- name = waypoint.name;
- type = "waypoint";
- typeSpec = waypoint.type;
- }
-}
+package cgeo.geocaching; + +import cgeo.geocaching.geopoint.Geopoint; + +public class cgCoord { + + public Integer id = null; + public String geocode = ""; + public String type = "cache"; + public String typeSpec = "traditional"; + public String name = ""; + public boolean found = false; + public boolean disabled = false; + public Geopoint coords = new Geopoint(0, 0); + public Float difficulty = null; + public Float terrain = null; + public String size = null; + + public cgCoord() { + } + + public cgCoord(cgCache cache) { + disabled = cache.disabled; + found = cache.found; + geocode = cache.geocode; + coords = cache.coords; + name = cache.name; + type = "cache"; + typeSpec = cache.type; + difficulty = cache.difficulty; + terrain = cache.terrain; + size = cache.size; + } + + public cgCoord(cgWaypoint waypoint) { + id = waypoint.id; + disabled = false; + found = false; + geocode = ""; + coords = waypoint.coords; + name = waypoint.name; + type = "waypoint"; + typeSpec = waypoint.type; + } +} diff --git a/src/cgeo/geocaching/cgDirection.java b/src/cgeo/geocaching/cgDirection.java index c1c1f68..21ea883 100644 --- a/src/cgeo/geocaching/cgDirection.java +++ b/src/cgeo/geocaching/cgDirection.java @@ -1,75 +1,75 @@ -package cgeo.geocaching;
-
-import cgeo.geocaching.compatibility.Compatibility;
-
-import android.app.Activity;
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-
-public class cgDirection {
- private cgDirection dir = null;
- private Context context = null;
- private SensorManager sensorManager = null;
- private cgeoSensorListener sensorListener = null;
- private cgUpdateDir dirUpdate = null;
-
- public Float directionNow = null;
-
- public cgDirection(Context contextIn, cgUpdateDir dirUpdateIn) {
- context = contextIn;
- dirUpdate = dirUpdateIn;
- sensorListener = new cgeoSensorListener();
- }
-
- public void initDir() {
- dir = this;
-
- if (sensorManager == null) {
- sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
- }
- sensorManager.registerListener(sensorListener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL);
- }
-
- public void closeDir() {
- if (sensorManager != null && sensorListener != null) {
- sensorManager.unregisterListener(sensorListener);
- }
- }
-
- public void replaceUpdate(cgUpdateDir dirUpdateIn) {
- dirUpdate = dirUpdateIn;
-
- if (dirUpdate != null && directionNow != null) {
- dirUpdate.updateDir(dir);
- }
- }
-
- private class cgeoSensorListener implements SensorEventListener {
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- /*
- * There is a bug in Android, which appearently causes this method to be called every
- * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging
- * this event leads to the log being flooded with multiple entries _per second_,
- * which I experienced when running cgeo in a building (with GPS and network being
- * unreliable).
- *
- * See for example https://code.google.com/p/android/issues/detail?id=14792
- */
-
- //Log.i(cgSettings.tag, "Compass' accuracy is low (" + accuracy + ")");
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- directionNow = Compatibility.getDirectionNow(event.values[0], (Activity) context);
-
- if (dirUpdate != null && directionNow != null) {
- dirUpdate.updateDir(dir);
- }
- }
- }
-}
+package cgeo.geocaching; + +import cgeo.geocaching.compatibility.Compatibility; + +import android.app.Activity; +import android.content.Context; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; + +public class cgDirection { + private cgDirection dir = null; + private Context context = null; + private SensorManager sensorManager = null; + private cgeoSensorListener sensorListener = null; + private cgUpdateDir dirUpdate = null; + + public Float directionNow = null; + + public cgDirection(Context contextIn, cgUpdateDir dirUpdateIn) { + context = contextIn; + dirUpdate = dirUpdateIn; + sensorListener = new cgeoSensorListener(); + } + + public void initDir() { + dir = this; + + if (sensorManager == null) { + sensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE); + } + sensorManager.registerListener(sensorListener, sensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION), SensorManager.SENSOR_DELAY_NORMAL); + } + + public void closeDir() { + if (sensorManager != null && sensorListener != null) { + sensorManager.unregisterListener(sensorListener); + } + } + + public void replaceUpdate(cgUpdateDir dirUpdateIn) { + dirUpdate = dirUpdateIn; + + if (dirUpdate != null && directionNow != null) { + dirUpdate.updateDir(dir); + } + } + + private class cgeoSensorListener implements SensorEventListener { + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + /* + * There is a bug in Android, which appearently causes this method to be called every + * time the sensor _value_ changed, even if the _accuracy_ did not change. So logging + * this event leads to the log being flooded with multiple entries _per second_, + * which I experienced when running cgeo in a building (with GPS and network being + * unreliable). + * + * See for example https://code.google.com/p/android/issues/detail?id=14792 + */ + + //Log.i(cgSettings.tag, "Compass' accuracy is low (" + accuracy + ")"); + } + + @Override + public void onSensorChanged(SensorEvent event) { + directionNow = Compatibility.getDirectionNow(event.values[0], (Activity) context); + + if (dirUpdate != null && directionNow != null) { + dirUpdate.updateDir(dir); + } + } + } +} diff --git a/src/cgeo/geocaching/enumerations/CacheSize.java b/src/cgeo/geocaching/enumerations/CacheSize.java index 901be9e..9ea82bb 100644 --- a/src/cgeo/geocaching/enumerations/CacheSize.java +++ b/src/cgeo/geocaching/enumerations/CacheSize.java @@ -1,37 +1,37 @@ -package cgeo.geocaching.enumerations;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Enum listing cache sizes
- *
- * @author koem
- */
-public enum CacheSize {
- MICRO("micro", 1),
- SMALL("small", 2),
- REGULAR("regular", 3),
- LARGE("large", 4),
- NOT_CHOSEN("not chosen", 0),
- OTHER("other", 0);
-
- public final String cgeoId;
- public final int comparable;
-
- private CacheSize(String cgeoId, int comparable) {
- this.cgeoId = cgeoId;
- this.comparable = comparable;
- }
-
- final public static Map<String, CacheSize> FIND_BY_CGEOID;
- static {
- final HashMap<String, CacheSize> mapping = new HashMap<String, CacheSize>();
- for (CacheSize cs : values()) {
- mapping.put(cs.cgeoId, cs);
- }
- FIND_BY_CGEOID = Collections.unmodifiableMap(mapping);
- }
-
-}
+package cgeo.geocaching.enumerations; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Enum listing cache sizes + * + * @author koem + */ +public enum CacheSize { + MICRO("micro", 1), + SMALL("small", 2), + REGULAR("regular", 3), + LARGE("large", 4), + NOT_CHOSEN("not chosen", 0), + OTHER("other", 0); + + public final String cgeoId; + public final int comparable; + + private CacheSize(String cgeoId, int comparable) { + this.cgeoId = cgeoId; + this.comparable = comparable; + } + + final public static Map<String, CacheSize> FIND_BY_CGEOID; + static { + final HashMap<String, CacheSize> mapping = new HashMap<String, CacheSize>(); + for (CacheSize cs : values()) { + mapping.put(cs.cgeoId, cs); + } + FIND_BY_CGEOID = Collections.unmodifiableMap(mapping); + } + +} diff --git a/src/cgeo/geocaching/enumerations/CacheType.java b/src/cgeo/geocaching/enumerations/CacheType.java index f30afc9..6acc3a4 100644 --- a/src/cgeo/geocaching/enumerations/CacheType.java +++ b/src/cgeo/geocaching/enumerations/CacheType.java @@ -1,52 +1,52 @@ -package cgeo.geocaching.enumerations;
-
-import cgeo.geocaching.R;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Enum listing all cache types
- *
- * @author koem
- */
-public enum CacheType {
- TRADITIONAL ("traditional", "traditional cache", "32bc9333-5e52-4957-b0f6-5a2c8fc7b257", R.string.traditional),
- MULTI ("multi", "multi-cache", "a5f6d0ad-d2f2-4011-8c14-940a9ebf3c74", R.string.multi),
- MYSTERY ("mystery", "unknown cache", "40861821-1835-4e11-b666-8d41064d03fe", R.string.mystery),
- LETTERBOX ("letterbox", "letterbox hybrid", "4bdd8fb2-d7bc-453f-a9c5-968563b15d24", R.string.letterbox),
- EVENT ("event", "event cache", "69eb8534-b718-4b35-ae3c-a856a55b0874", R.string.event),
- MEGA_EVENT ("mega", "mega-event cache", "69eb8535-b718-4b35-ae3c-a856a55b0874", R.string.mega),
- EARTH ("earth", "earthcache", "c66f5cf3-9523-4549-b8dd-759cd2f18db8", R.string.earth),
- CITO ("cito", "cache in trash out event", "57150806-bc1a-42d6-9cf0-538d171a2d22", R.string.cito),
- WEBCAM ("webcam", "webcam cache", "31d2ae3c-c358-4b5f-8dcd-2185bf472d3d", R.string.webcam),
- VIRTUAL ("virtual", "virtual cache", "294d4360-ac86-4c83-84dd-8113ef678d7e", R.string.virtual),
- WHERIGO ("wherigo", "wherigo cache", "0544fa55-772d-4e5c-96a9-36a51ebcf5c9", R.string.wherigo),
- LOSTANDFOUND ("lostfound", "lost & found", "3ea6533d-bb52-42fe-b2d2-79a3424d4728", R.string.lostfound),
- PROJECT_APE ("ape", "project ape cache", "2555690d-b2bc-4b55-b5ac-0cb704c0b768", R.string.ape),
- GCHQ ("gchq", "groundspeak hq", "416f2494-dc17-4b6a-9bab-1a29dd292d8c", R.string.gchq),
- GPS_EXHIBIT ("gps", "gps cache exhibit", "72e69af2-7986-4990-afd9-bc16cbbb4ce3", R.string.gps);
-
- public final String cgeoId;
- public final String pattern;
- public final String guid;
- public final int stringId;
-
- private CacheType(String cgeoId, String pattern, String guid, int stringId) {
- this.cgeoId = cgeoId;
- this.pattern = pattern;
- this.guid = guid;
- this.stringId = stringId;
- }
-
- public final static Map<String, CacheType> FIND_BY_CGEOID;
- static {
- final HashMap<String, CacheType> mapping = new HashMap<String, CacheType>();
- for (CacheType ct : values()) {
- mapping.put(ct.cgeoId, ct);
- }
- FIND_BY_CGEOID = Collections.unmodifiableMap(mapping);
- }
-
-}
+package cgeo.geocaching.enumerations; + +import cgeo.geocaching.R; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Enum listing all cache types + * + * @author koem + */ +public enum CacheType { + TRADITIONAL ("traditional", "traditional cache", "32bc9333-5e52-4957-b0f6-5a2c8fc7b257", R.string.traditional), + MULTI ("multi", "multi-cache", "a5f6d0ad-d2f2-4011-8c14-940a9ebf3c74", R.string.multi), + MYSTERY ("mystery", "unknown cache", "40861821-1835-4e11-b666-8d41064d03fe", R.string.mystery), + LETTERBOX ("letterbox", "letterbox hybrid", "4bdd8fb2-d7bc-453f-a9c5-968563b15d24", R.string.letterbox), + EVENT ("event", "event cache", "69eb8534-b718-4b35-ae3c-a856a55b0874", R.string.event), + MEGA_EVENT ("mega", "mega-event cache", "69eb8535-b718-4b35-ae3c-a856a55b0874", R.string.mega), + EARTH ("earth", "earthcache", "c66f5cf3-9523-4549-b8dd-759cd2f18db8", R.string.earth), + CITO ("cito", "cache in trash out event", "57150806-bc1a-42d6-9cf0-538d171a2d22", R.string.cito), + WEBCAM ("webcam", "webcam cache", "31d2ae3c-c358-4b5f-8dcd-2185bf472d3d", R.string.webcam), + VIRTUAL ("virtual", "virtual cache", "294d4360-ac86-4c83-84dd-8113ef678d7e", R.string.virtual), + WHERIGO ("wherigo", "wherigo cache", "0544fa55-772d-4e5c-96a9-36a51ebcf5c9", R.string.wherigo), + LOSTANDFOUND ("lostfound", "lost & found", "3ea6533d-bb52-42fe-b2d2-79a3424d4728", R.string.lostfound), + PROJECT_APE ("ape", "project ape cache", "2555690d-b2bc-4b55-b5ac-0cb704c0b768", R.string.ape), + GCHQ ("gchq", "groundspeak hq", "416f2494-dc17-4b6a-9bab-1a29dd292d8c", R.string.gchq), + GPS_EXHIBIT ("gps", "gps cache exhibit", "72e69af2-7986-4990-afd9-bc16cbbb4ce3", R.string.gps); + + public final String cgeoId; + public final String pattern; + public final String guid; + public final int stringId; + + private CacheType(String cgeoId, String pattern, String guid, int stringId) { + this.cgeoId = cgeoId; + this.pattern = pattern; + this.guid = guid; + this.stringId = stringId; + } + + public final static Map<String, CacheType> FIND_BY_CGEOID; + static { + final HashMap<String, CacheType> mapping = new HashMap<String, CacheType>(); + for (CacheType ct : values()) { + mapping.put(ct.cgeoId, ct); + } + FIND_BY_CGEOID = Collections.unmodifiableMap(mapping); + } + +} diff --git a/src/cgeo/geocaching/enumerations/WaypointType.java b/src/cgeo/geocaching/enumerations/WaypointType.java index cd4b31c..84ef59a 100644 --- a/src/cgeo/geocaching/enumerations/WaypointType.java +++ b/src/cgeo/geocaching/enumerations/WaypointType.java @@ -1,36 +1,36 @@ -package cgeo.geocaching.enumerations;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Enum listing waypoint types
- *
- * @author koem
- */
-public enum WaypointType {
- FLAG ("flag"),
- OWN ("own"),
- PKG ("pkg"),
- PUZZLE ("puzzle"),
- STAGE ("stage"),
- TRAILHEAD ("trailhead"),
- WAYPOINT ("waypoint");
-
- public final String cgeoId;
-
- private WaypointType(String cgeoId) {
- this.cgeoId = cgeoId;
- }
-
- public static final Map<String, WaypointType> FIND_BY_CGEOID;
- static {
- final HashMap<String, WaypointType> mapping = new HashMap<String, WaypointType>();
- for (WaypointType wt : values()) {
- mapping.put(wt.cgeoId, wt);
- }
- FIND_BY_CGEOID = Collections.unmodifiableMap(mapping);
- }
-
-}
+package cgeo.geocaching.enumerations; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +/** + * Enum listing waypoint types + * + * @author koem + */ +public enum WaypointType { + FLAG ("flag"), + OWN ("own"), + PKG ("pkg"), + PUZZLE ("puzzle"), + STAGE ("stage"), + TRAILHEAD ("trailhead"), + WAYPOINT ("waypoint"); + + public final String cgeoId; + + private WaypointType(String cgeoId) { + this.cgeoId = cgeoId; + } + + public static final Map<String, WaypointType> FIND_BY_CGEOID; + static { + final HashMap<String, WaypointType> mapping = new HashMap<String, WaypointType>(); + for (WaypointType wt : values()) { + mapping.put(wt.cgeoId, wt); + } + FIND_BY_CGEOID = Collections.unmodifiableMap(mapping); + } + +} diff --git a/src/cgeo/geocaching/files/FileParser.java b/src/cgeo/geocaching/files/FileParser.java index c7f16c8..158d390 100644 --- a/src/cgeo/geocaching/files/FileParser.java +++ b/src/cgeo/geocaching/files/FileParser.java @@ -1,53 +1,53 @@ -package cgeo.geocaching.files;
-
-import cgeo.geocaching.cgBase;
-import cgeo.geocaching.cgCache;
-import cgeo.geocaching.cgSearch;
-
-import android.os.Handler;
-import android.os.Message;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.Date;
-
-public abstract class FileParser {
- protected static StringBuilder readFile(File file)
- throws FileNotFoundException, IOException {
- StringBuilder buffer = new StringBuilder();
- BufferedReader input = new BufferedReader(new FileReader(file));
- try {
- String line = null;
- while ((line = input.readLine()) != null) {
- buffer.append(line);
- }
- } finally {
- input.close();
- }
- return buffer;
- }
-
- static void showFinishedMessage(Handler handler, cgSearch search) {
- if (handler != null) {
- final Message msg = new Message();
- msg.obj = search.getCount();
- handler.sendMessage(msg);
- }
- }
-
- protected static void fixCache(cgCache cache) {
- cache.latitudeString = cgBase.formatLatitude(cache.coords.getLatitude(), true);
- cache.longitudeString = cgBase.formatLongitude(cache.coords.getLongitude(), true);
- if (cache.inventory != null) {
- cache.inventoryItems = cache.inventory.size();
- } else {
- cache.inventoryItems = 0;
- }
- cache.updated = new Date().getTime();
- cache.detailedUpdate = new Date().getTime();
- }
-
-}
+package cgeo.geocaching.files; + +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgSearch; + +import android.os.Handler; +import android.os.Message; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.util.Date; + +public abstract class FileParser { + protected static StringBuilder readFile(File file) + throws FileNotFoundException, IOException { + StringBuilder buffer = new StringBuilder(); + BufferedReader input = new BufferedReader(new FileReader(file)); + try { + String line = null; + while ((line = input.readLine()) != null) { + buffer.append(line); + } + } finally { + input.close(); + } + return buffer; + } + + static void showFinishedMessage(Handler handler, cgSearch search) { + if (handler != null) { + final Message msg = new Message(); + msg.obj = search.getCount(); + handler.sendMessage(msg); + } + } + + protected static void fixCache(cgCache cache) { + cache.latitudeString = cgBase.formatLatitude(cache.coords.getLatitude(), true); + cache.longitudeString = cgBase.formatLongitude(cache.coords.getLongitude(), true); + if (cache.inventory != null) { + cache.inventoryItems = cache.inventory.size(); + } else { + cache.inventoryItems = 0; + } + cache.updated = new Date().getTime(); + cache.detailedUpdate = new Date().getTime(); + } + +} diff --git a/src/cgeo/geocaching/files/GPX10Parser.java b/src/cgeo/geocaching/files/GPX10Parser.java index be3c857..c3b95b2 100644 --- a/src/cgeo/geocaching/files/GPX10Parser.java +++ b/src/cgeo/geocaching/files/GPX10Parser.java @@ -1,20 +1,20 @@ -package cgeo.geocaching.files;
-
-import cgeo.geocaching.cgSearch;
-import cgeo.geocaching.cgeoapplication;
-
-import android.sax.Element;
-
-public final class GPX10Parser extends GPXParser {
-
- public GPX10Parser(cgeoapplication appIn, int listIdIn,
- cgSearch searchIn) {
- super(appIn, listIdIn, searchIn, "http://www.topografix.com/GPX/1/0", "1.0");
- }
-
- @Override
- protected Element getCacheParent(Element waypoint) {
- return waypoint;
- }
-
-}
+package cgeo.geocaching.files; + +import cgeo.geocaching.cgSearch; +import cgeo.geocaching.cgeoapplication; + +import android.sax.Element; + +public final class GPX10Parser extends GPXParser { + + public GPX10Parser(cgeoapplication appIn, int listIdIn, + cgSearch searchIn) { + super(appIn, listIdIn, searchIn, "http://www.topografix.com/GPX/1/0", "1.0"); + } + + @Override + protected Element getCacheParent(Element waypoint) { + return waypoint; + } + +} diff --git a/src/cgeo/geocaching/files/GPX11Parser.java b/src/cgeo/geocaching/files/GPX11Parser.java index 9370450..4c8960b 100644 --- a/src/cgeo/geocaching/files/GPX11Parser.java +++ b/src/cgeo/geocaching/files/GPX11Parser.java @@ -1,20 +1,20 @@ -package cgeo.geocaching.files;
-
-import cgeo.geocaching.cgSearch;
-import cgeo.geocaching.cgeoapplication;
-
-import android.sax.Element;
-
-public final class GPX11Parser extends GPXParser {
-
- public GPX11Parser(cgeoapplication appIn, int listIdIn,
- cgSearch searchIn) {
- super(appIn, listIdIn, searchIn, "http://www.topografix.com/GPX/1/1", "1.1");
- }
-
- @Override
- protected Element getCacheParent(Element waypoint) {
- return waypoint.getChild(namespace, "extensions");
- }
-
-}
+package cgeo.geocaching.files; + +import cgeo.geocaching.cgSearch; +import cgeo.geocaching.cgeoapplication; + +import android.sax.Element; + +public final class GPX11Parser extends GPXParser { + + public GPX11Parser(cgeoapplication appIn, int listIdIn, + cgSearch searchIn) { + super(appIn, listIdIn, searchIn, "http://www.topografix.com/GPX/1/1", "1.1"); + } + + @Override + protected Element getCacheParent(Element waypoint) { + return waypoint.getChild(namespace, "extensions"); + } + +} diff --git a/src/cgeo/geocaching/files/LocParser.java b/src/cgeo/geocaching/files/LocParser.java index f631e10..0cbb297 100644 --- a/src/cgeo/geocaching/files/LocParser.java +++ b/src/cgeo/geocaching/files/LocParser.java @@ -1,179 +1,179 @@ -package cgeo.geocaching.files;
-
-import cgeo.geocaching.cgBase;
-import cgeo.geocaching.cgCache;
-import cgeo.geocaching.cgCacheWrap;
-import cgeo.geocaching.cgCoord;
-import cgeo.geocaching.cgSearch;
-import cgeo.geocaching.cgSettings;
-import cgeo.geocaching.cgeoapplication;
-import cgeo.geocaching.geopoint.Geopoint;
-
-import org.apache.commons.lang3.StringUtils;
-
-import android.os.Handler;
-import android.util.Log;
-
-import java.io.File;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.UUID;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class LocParser extends FileParser {
- private static final Pattern patternGeocode = Pattern
- .compile("name id=\"([^\"]+)\"");
- private static final Pattern patternLat = Pattern
- .compile("lat=\"([^\"]+)\"");
- private static final Pattern patternLon = Pattern
- .compile("lon=\"([^\"]+)\"");
- // premium only >>
- private static final Pattern patternDifficulty = Pattern
- .compile("<difficulty>([^<]+)</difficulty>");
- private static final Pattern patternTerrain = Pattern
- .compile("<terrain>([^<]+)</terrain>");
- private static final Pattern patternContainer = Pattern
- .compile("<container>([^<]+)</container>");
- private static final Pattern patternName = Pattern.compile("CDATA\\[([^\\]]+)\\]");
-
- public static void parseLoc(final cgCacheWrap caches,
- final String fileContent) {
- final Map<String, cgCoord> cidCoords = parseCoordinates(fileContent);
-
- // save found cache coordinates
- for (cgCache cache : caches.cacheList) {
- if (cidCoords.containsKey(cache.geocode)) {
- cgCoord coord = cidCoords.get(cache.geocode);
-
- copyCoordToCache(coord, cache);
- }
- }
- }
-
- private static void copyCoordToCache(final cgCoord coord, final cgCache cache) {
- cache.coords = coord.coords;
- cache.difficulty = coord.difficulty;
- cache.terrain = coord.terrain;
- cache.size = coord.size;
- cache.geocode = coord.geocode.toUpperCase();
- if (StringUtils.isBlank(cache.name)) {
- cache.name = coord.name;
- }
- }
-
- private static Map<String, cgCoord> parseCoordinates(
- final String fileContent) {
- final Map<String, cgCoord> coords = new HashMap<String, cgCoord>();
- if (StringUtils.isBlank(fileContent)) {
- return coords;
- }
- // >> premium only
-
- final String[] points = fileContent.split("<waypoint>");
-
- // parse coordinates
- for (String pointString : points) {
- final cgCoord pointCoord = new cgCoord();
-
- final Matcher matcherGeocode = patternGeocode.matcher(pointString);
- if (matcherGeocode.find()) {
- String geocode = matcherGeocode.group(1).trim().toUpperCase();
- pointCoord.name = geocode;
- pointCoord.geocode = geocode;
- }
- final Matcher matcherName = patternName.matcher(pointString);
- if (matcherName.find()) {
- String name = matcherName.group(1).trim();
- int pos = name.indexOf(" by ");
- if (pos > 0) {
- name = name.substring(0, pos).trim();
- }
- pointCoord.name = name;
- }
- final Matcher matcherLat = patternLat.matcher(pointString);
- final Matcher matcherLon = patternLon.matcher(pointString);
- if (matcherLat.find() && matcherLon.find()) {
- final Map<String, Object> tmpLat = cgBase.parseCoordinate(matcherLat.group(1).trim(), "lat");
- final Map<String, Object> tmpLon = cgBase.parseCoordinate(matcherLon.group(1).trim(), "lon");
- pointCoord.coords = new Geopoint((Double) tmpLat.get("coordinate"),
- (Double) tmpLon.get("coordinate"));
- }
- final Matcher matcherDifficulty = patternDifficulty.matcher(pointString);
- if (matcherDifficulty.find()) {
- pointCoord.difficulty = new Float(matcherDifficulty.group(1)
- .trim());
- }
- final Matcher matcherTerrain = patternTerrain.matcher(pointString);
- if (matcherTerrain.find()) {
- pointCoord.terrain = new Float(matcherTerrain.group(1).trim());
- }
- final Matcher matcherContainer = patternContainer.matcher(pointString);
- if (matcherContainer.find()) {
- final int size = Integer.parseInt(matcherContainer.group(1)
- .trim());
-
- if (size == 1) {
- pointCoord.size = "not chosen";
- } else if (size == 2) {
- pointCoord.size = "micro";
- } else if (size == 3) {
- pointCoord.size = "regular";
- } else if (size == 4) {
- pointCoord.size = "large";
- } else if (size == 5) {
- pointCoord.size = "virtual";
- } else if (size == 6) {
- pointCoord.size = "other";
- } else if (size == 8) {
- pointCoord.size = "small";
- } else {
- pointCoord.size = "unknown";
- }
- }
-
- if (StringUtils.isNotBlank(pointCoord.geocode)) {
- coords.put(pointCoord.geocode, pointCoord);
- }
- }
-
- Log.i(cgSettings.tag,
- "Coordinates found in .loc file: " + coords.size());
- return coords;
- }
-
- public static UUID parseLoc(cgeoapplication app, File file, int listId,
- Handler handler) {
- cgSearch search = new cgSearch();
- UUID searchId = null;
-
- try {
- Map<String, cgCoord> coords = parseCoordinates(readFile(file).toString());
- final cgCacheWrap caches = new cgCacheWrap();
- for (Entry<String, cgCoord> entry : coords.entrySet()) {
- cgCoord coord = entry.getValue();
- if (StringUtils.isBlank(coord.geocode) || StringUtils.isBlank(coord.name)) {
- continue;
- }
- cgCache cache = new cgCache();
- copyCoordToCache(coord, cache);
- caches.cacheList.add(cache);
-
- fixCache(cache);
- cache.reason = listId;
- cache.detailed = false;
-
- app.addCacheToSearch(search, cache);
- }
- caches.totalCnt = caches.cacheList.size();
- showFinishedMessage(handler, search);
- } catch (Exception e) {
- Log.e(cgSettings.tag, "cgBase.parseGPX: " + e.toString());
- }
-
- Log.i(cgSettings.tag, "Caches found in .gpx file: " + app.getCount(searchId));
-
- return search.getCurrentId();
- }
-}
+package cgeo.geocaching.files; + +import cgeo.geocaching.cgBase; +import cgeo.geocaching.cgCache; +import cgeo.geocaching.cgCacheWrap; +import cgeo.geocaching.cgCoord; +import cgeo.geocaching.cgSearch; +import cgeo.geocaching.cgSettings; +import cgeo.geocaching.cgeoapplication; +import cgeo.geocaching.geopoint.Geopoint; + +import org.apache.commons.lang3.StringUtils; + +import android.os.Handler; +import android.util.Log; + +import java.io.File; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public final class LocParser extends FileParser { + private static final Pattern patternGeocode = Pattern + .compile("name id=\"([^\"]+)\""); + private static final Pattern patternLat = Pattern + .compile("lat=\"([^\"]+)\""); + private static final Pattern patternLon = Pattern + .compile("lon=\"([^\"]+)\""); + // premium only >> + private static final Pattern patternDifficulty = Pattern + .compile("<difficulty>([^<]+)</difficulty>"); + private static final Pattern patternTerrain = Pattern + .compile("<terrain>([^<]+)</terrain>"); + private static final Pattern patternContainer = Pattern + .compile("<container>([^<]+)</container>"); + private static final Pattern patternName = Pattern.compile("CDATA\\[([^\\]]+)\\]"); + + public static void parseLoc(final cgCacheWrap caches, + final String fileContent) { + final Map<String, cgCoord> cidCoords = parseCoordinates(fileContent); + + // save found cache coordinates + for (cgCache cache : caches.cacheList) { + if (cidCoords.containsKey(cache.geocode)) { + cgCoord coord = cidCoords.get(cache.geocode); + + copyCoordToCache(coord, cache); + } + } + } + + private static void copyCoordToCache(final cgCoord coord, final cgCache cache) { + cache.coords = coord.coords; + cache.difficulty = coord.difficulty; + cache.terrain = coord.terrain; + cache.size = coord.size; + cache.geocode = coord.geocode.toUpperCase(); + if (StringUtils.isBlank(cache.name)) { + cache.name = coord.name; + } + } + + private static Map<String, cgCoord> parseCoordinates( + final String fileContent) { + final Map<String, cgCoord> coords = new HashMap<String, cgCoord>(); + if (StringUtils.isBlank(fileContent)) { + return coords; + } + // >> premium only + + final String[] points = fileContent.split("<waypoint>"); + + // parse coordinates + for (String pointString : points) { + final cgCoord pointCoord = new cgCoord(); + + final Matcher matcherGeocode = patternGeocode.matcher(pointString); + if (matcherGeocode.find()) { + String geocode = matcherGeocode.group(1).trim().toUpperCase(); + pointCoord.name = geocode; + pointCoord.geocode = geocode; + } + final Matcher matcherName = patternName.matcher(pointString); + if (matcherName.find()) { + String name = matcherName.group(1).trim(); + int pos = name.indexOf(" by "); + if (pos > 0) { + name = name.substring(0, pos).trim(); + } + pointCoord.name = name; + } + final Matcher matcherLat = patternLat.matcher(pointString); + final Matcher matcherLon = patternLon.matcher(pointString); + if (matcherLat.find() && matcherLon.find()) { + final Map<String, Object> tmpLat = cgBase.parseCoordinate(matcherLat.group(1).trim(), "lat"); + final Map<String, Object> tmpLon = cgBase.parseCoordinate(matcherLon.group(1).trim(), "lon"); + pointCoord.coords = new Geopoint((Double) tmpLat.get("coordinate"), + (Double) tmpLon.get("coordinate")); + } + final Matcher matcherDifficulty = patternDifficulty.matcher(pointString); + if (matcherDifficulty.find()) { + pointCoord.difficulty = new Float(matcherDifficulty.group(1) + .trim()); + } + final Matcher matcherTerrain = patternTerrain.matcher(pointString); + if (matcherTerrain.find()) { + pointCoord.terrain = new Float(matcherTerrain.group(1).trim()); + } + final Matcher matcherContainer = patternContainer.matcher(pointString); + if (matcherContainer.find()) { + final int size = Integer.parseInt(matcherContainer.group(1) + .trim()); + + if (size == 1) { + pointCoord.size = "not chosen"; + } else if (size == 2) { + pointCoord.size = "micro"; + } else if (size == 3) { + pointCoord.size = "regular"; + } else if (size == 4) { + pointCoord.size = "large"; + } else if (size == 5) { + pointCoord.size = "virtual"; + } else if (size == 6) { + pointCoord.size = "other"; + } else if (size == 8) { + pointCoord.size = "small"; + } else { + pointCoord.size = "unknown"; + } + } + + if (StringUtils.isNotBlank(pointCoord.geocode)) { + coords.put(pointCoord.geocode, pointCoord); + } + } + + Log.i(cgSettings.tag, + "Coordinates found in .loc file: " + coords.size()); + return coords; + } + + public static UUID parseLoc(cgeoapplication app, File file, int listId, + Handler handler) { + cgSearch search = new cgSearch(); + UUID searchId = null; + + try { + Map<String, cgCoord> coords = parseCoordinates(readFile(file).toString()); + final cgCacheWrap caches = new cgCacheWrap(); + for (Entry<String, cgCoord> entry : coords.entrySet()) { + cgCoord coord = entry.getValue(); + if (StringUtils.isBlank(coord.geocode) || StringUtils.isBlank(coord.name)) { + continue; + } + cgCache cache = new cgCache(); + copyCoordToCache(coord, cache); + caches.cacheList.add(cache); + + fixCache(cache); + cache.reason = listId; + cache.detailed = false; + + app.addCacheToSearch(search, cache); + } + caches.totalCnt = caches.cacheList.size(); + showFinishedMessage(handler, search); + } catch (Exception e) { + Log.e(cgSettings.tag, "cgBase.parseGPX: " + e.toString()); + } + + Log.i(cgSettings.tag, "Caches found in .gpx file: " + app.getCount(searchId)); + + return search.getCurrentId(); + } +} diff --git a/src/cgeo/geocaching/utils/CollectionUtils.java b/src/cgeo/geocaching/utils/CollectionUtils.java index b7c6f3f..06e2419 100644 --- a/src/cgeo/geocaching/utils/CollectionUtils.java +++ b/src/cgeo/geocaching/utils/CollectionUtils.java @@ -1,24 +1,24 @@ -package cgeo.geocaching.utils;
-
-import java.util.List;
-import java.util.Map;
-
-public class CollectionUtils {
-
- public static <T> boolean isEmpty(List<T> list) {
- return (list != null && list.size() == 0);
- }
-
- public static <T, T2> boolean isEmpty(Map<T, T2> map) {
- return (map != null && map.size() == 0);
- }
-
- public static <T> boolean isNotEmpty(List<T> list) {
- return (list != null && list.size() != 0);
- }
-
- public static <T, T2> boolean isNotEmpty(Map<T, T2> map) {
- return (map != null && map.size() != 0);
- }
-
-}
+package cgeo.geocaching.utils; + +import java.util.List; +import java.util.Map; + +public class CollectionUtils { + + public static <T> boolean isEmpty(List<T> list) { + return (list != null && list.size() == 0); + } + + public static <T, T2> boolean isEmpty(Map<T, T2> map) { + return (map != null && map.size() == 0); + } + + public static <T> boolean isNotEmpty(List<T> list) { + return (list != null && list.size() != 0); + } + + public static <T, T2> boolean isNotEmpty(Map<T, T2> map) { + return (map != null && map.size() != 0); + } + +} diff --git a/src/org/apache/commons/lang3/ArrayUtils.java b/src/org/apache/commons/lang3/ArrayUtils.java index 352d7e0..fa8b04b 100644 --- a/src/org/apache/commons/lang3/ArrayUtils.java +++ b/src/org/apache/commons/lang3/ArrayUtils.java @@ -1,5796 +1,5796 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.lang.reflect.Array;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-
-import org.apache.commons.lang3.builder.EqualsBuilder;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import org.apache.commons.lang3.mutable.MutableInt;
-
-/**
- * <p>Operations on arrays, primitive arrays (like {@code int[]}) and
- * primitive wrapper arrays (like {@code Integer[]}).</p>
- *
- * <p>This class tries to handle {@code null} input gracefully.
- * An exception will not be thrown for a {@code null}
- * array input. However, an Object array that contains a {@code null}
- * element may throw an exception. Each method documents its behaviour.</p>
- *
- * <p>#ThreadSafe#</p>
- * @since 2.0
- * @version $Id$
- */
-public class ArrayUtils {
-
- /**
- * An empty immutable {@code Object} array.
- */
- public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
- /**
- * An empty immutable {@code Class} array.
- */
- public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0];
- /**
- * An empty immutable {@code String} array.
- */
- public static final String[] EMPTY_STRING_ARRAY = new String[0];
- /**
- * An empty immutable {@code long} array.
- */
- public static final long[] EMPTY_LONG_ARRAY = new long[0];
- /**
- * An empty immutable {@code Long} array.
- */
- public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0];
- /**
- * An empty immutable {@code int} array.
- */
- public static final int[] EMPTY_INT_ARRAY = new int[0];
- /**
- * An empty immutable {@code Integer} array.
- */
- public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0];
- /**
- * An empty immutable {@code short} array.
- */
- public static final short[] EMPTY_SHORT_ARRAY = new short[0];
- /**
- * An empty immutable {@code Short} array.
- */
- public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0];
- /**
- * An empty immutable {@code byte} array.
- */
- public static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
- /**
- * An empty immutable {@code Byte} array.
- */
- public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0];
- /**
- * An empty immutable {@code double} array.
- */
- public static final double[] EMPTY_DOUBLE_ARRAY = new double[0];
- /**
- * An empty immutable {@code Double} array.
- */
- public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0];
- /**
- * An empty immutable {@code float} array.
- */
- public static final float[] EMPTY_FLOAT_ARRAY = new float[0];
- /**
- * An empty immutable {@code Float} array.
- */
- public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0];
- /**
- * An empty immutable {@code boolean} array.
- */
- public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0];
- /**
- * An empty immutable {@code Boolean} array.
- */
- public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0];
- /**
- * An empty immutable {@code char} array.
- */
- public static final char[] EMPTY_CHAR_ARRAY = new char[0];
- /**
- * An empty immutable {@code Character} array.
- */
- public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0];
-
- /**
- * The index value when an element is not found in a list or array: {@code -1}.
- * This value is returned by methods in this class and can also be used in comparisons with values returned by
- * various method from {@link java.util.List}.
- */
- public static final int INDEX_NOT_FOUND = -1;
-
- /**
- * <p>ArrayUtils instances should NOT be constructed in standard programming.
- * Instead, the class should be used as <code>ArrayUtils.clone(new int[] {2})</code>.</p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean instance
- * to operate.</p>
- */
- public ArrayUtils() {
- super();
- }
-
-
- // NOTE: Cannot use {@code} to enclose text which includes {}, but <code></code> is OK
-
-
- // Basic methods handling multi-dimensional arrays
- //-----------------------------------------------------------------------
- /**
- * <p>Outputs an array as a String, treating {@code null} as an empty array.</p>
- *
- * <p>Multi-dimensional arrays are handled correctly, including
- * multi-dimensional primitive arrays.</p>
- *
- * <p>The format is that of Java source code, for example <code>{a,b}</code>.</p>
- *
- * @param array the array to get a toString for, may be {@code null}
- * @return a String representation of the array, '{}' if null array input
- */
- public static String toString(Object array) {
- return toString(array, "{}");
- }
-
- /**
- * <p>Outputs an array as a String handling {@code null}s.</p>
- *
- * <p>Multi-dimensional arrays are handled correctly, including
- * multi-dimensional primitive arrays.</p>
- *
- * <p>The format is that of Java source code, for example <code>{a,b}</code>.</p>
- *
- * @param array the array to get a toString for, may be {@code null}
- * @param stringIfNull the String to return if the array is {@code null}
- * @return a String representation of the array
- */
- public static String toString(Object array, String stringIfNull) {
- if (array == null) {
- return stringIfNull;
- }
- return new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(array).toString();
- }
-
- /**
- * <p>Get a hash code for an array handling multi-dimensional arrays correctly.</p>
- *
- * <p>Multi-dimensional primitive arrays are also handled correctly by this method.</p>
- *
- * @param array the array to get a hash code for, {@code null} returns zero
- * @return a hash code for the array
- */
- public static int hashCode(Object array) {
- return new HashCodeBuilder().append(array).toHashCode();
- }
-
- /**
- * <p>Compares two arrays, using equals(), handling multi-dimensional arrays
- * correctly.</p>
- *
- * <p>Multi-dimensional primitive arrays are also handled correctly by this method.</p>
- *
- * @param array1 the left hand array to compare, may be {@code null}
- * @param array2 the right hand array to compare, may be {@code null}
- * @return {@code true} if the arrays are equal
- */
- public static boolean isEquals(Object array1, Object array2) {
- return new EqualsBuilder().append(array1, array2).isEquals();
- }
-
- // To map
- //-----------------------------------------------------------------------
- /**
- * <p>Converts the given array into a {@link java.util.Map}. Each element of the array
- * must be either a {@link java.util.Map.Entry} or an Array, containing at least two
- * elements, where the first element is used as key and the second as
- * value.</p>
- *
- * <p>This method can be used to initialize:</p>
- * <pre>
- * // Create a Map mapping colors.
- * Map colorMap = MapUtils.toMap(new String[][] {{
- * {"RED", "#FF0000"},
- * {"GREEN", "#00FF00"},
- * {"BLUE", "#0000FF"}});
- * </pre>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array an array whose elements are either a {@link java.util.Map.Entry} or
- * an Array containing at least two elements, may be {@code null}
- * @return a {@code Map} that was created from the array
- * @throws IllegalArgumentException if one element of this Array is
- * itself an Array containing less then two elements
- * @throws IllegalArgumentException if the array contains elements other
- * than {@link java.util.Map.Entry} and an Array
- */
- public static Map<Object, Object> toMap(Object[] array) {
- if (array == null) {
- return null;
- }
- final Map<Object, Object> map = new HashMap<Object, Object>((int) (array.length * 1.5));
- for (int i = 0; i < array.length; i++) {
- Object object = array[i];
- if (object instanceof Map.Entry<?, ?>) {
- Map.Entry<?,?> entry = (Map.Entry<?,?>) object;
- map.put(entry.getKey(), entry.getValue());
- } else if (object instanceof Object[]) {
- Object[] entry = (Object[]) object;
- if (entry.length < 2) {
- throw new IllegalArgumentException("Array element " + i + ", '"
- + object
- + "', has a length less than 2");
- }
- map.put(entry[0], entry[1]);
- } else {
- throw new IllegalArgumentException("Array element " + i + ", '"
- + object
- + "', is neither of type Map.Entry nor an Array");
- }
- }
- return map;
- }
-
- // Generic array
- //-----------------------------------------------------------------------
- /**
- * <p>Create a type-safe generic array.</p>
- *
- * <p>The Java language does not allow an array to be created from a generic type:</p>
- *
- * <pre>
- public static <T> T[] createAnArray(int size) {
- return new T[size]; // compiler error here
- }
- public static <T> T[] createAnArray(int size) {
- return (T[])new Object[size]; // ClassCastException at runtime
- }
- * </pre>
- *
- * <p>Therefore new arrays of generic types can be created with this method.
- * For example, an array of Strings can be created:</p>
- *
- * <pre>
- String[] array = ArrayUtils.toArray("1", "2");
- String[] emptyArray = ArrayUtils.<String>toArray();
- * </pre>
- *
- * <p>The method is typically used in scenarios, where the caller itself uses generic types
- * that have to be combined into an array.</p>
- *
- * <p>Note, this method makes only sense to provide arguments of the same type so that the
- * compiler can deduce the type of the array itself. While it is possible to select the
- * type explicitly like in
- * <code>Number[] array = ArrayUtils.<Number>toArray(Integer.valueOf(42), Double.valueOf(Math.PI))</code>,
- * there is no real advantage when compared to
- * <code>new Number[] {Integer.valueOf(42), Double.valueOf(Math.PI)}</code>.</p>
- *
- * @param <T> the array's element type
- * @param items the varargs array items, null allowed
- * @return the array, not null unless a null array is passed in
- * @since 3.0
- */
- public static <T> T[] toArray(final T... items) {
- return items;
- }
-
- // Clone
- //-----------------------------------------------------------------------
- /**
- * <p>Shallow clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>The objects in the array are not cloned, thus there is no special
- * handling for multi-dimensional arrays.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param <T> the component type of the array
- * @param array the array to shallow clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static <T> T[] clone(T[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static long[] clone(long[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static int[] clone(int[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static short[] clone(short[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static char[] clone(char[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static byte[] clone(byte[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static double[] clone(double[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static float[] clone(float[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- /**
- * <p>Clones an array returning a typecast result and handling
- * {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array the array to clone, may be {@code null}
- * @return the cloned array, {@code null} if {@code null} input
- */
- public static boolean[] clone(boolean[] array) {
- if (array == null) {
- return null;
- }
- return array.clone();
- }
-
- // nullToEmpty
- //-----------------------------------------------------------------------
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Object[] nullToEmpty(Object[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static String[] nullToEmpty(String[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_STRING_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static long[] nullToEmpty(long[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_LONG_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static int[] nullToEmpty(int[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_INT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static short[] nullToEmpty(short[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_SHORT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static char[] nullToEmpty(char[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_CHAR_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static byte[] nullToEmpty(byte[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_BYTE_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static double[] nullToEmpty(double[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_DOUBLE_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static float[] nullToEmpty(float[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_FLOAT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static boolean[] nullToEmpty(boolean[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_BOOLEAN_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Long[] nullToEmpty(Long[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_LONG_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Integer[] nullToEmpty(Integer[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_INTEGER_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Short[] nullToEmpty(Short[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_SHORT_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Character[] nullToEmpty(Character[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_CHARACTER_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Byte[] nullToEmpty(Byte[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_BYTE_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Double[] nullToEmpty(Double[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_DOUBLE_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Float[] nullToEmpty(Float[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_FLOAT_OBJECT_ARRAY;
- }
- return array;
- }
-
- /**
- * <p>Defensive programming technique to change a {@code null}
- * reference to an empty one.</p>
- *
- * <p>This method returns an empty array for a {@code null} input array.</p>
- *
- * <p>As a memory optimizing technique an empty array passed in will be overridden with
- * the empty {@code public static} references in this class.</p>
- *
- * @param array the array to check for {@code null} or empty
- * @return the same array, {@code public static} empty array if {@code null} or empty input
- * @since 2.5
- */
- public static Boolean[] nullToEmpty(Boolean[] array) {
- if (array == null || array.length == 0) {
- return EMPTY_BOOLEAN_OBJECT_ARRAY;
- }
- return array;
- }
-
- // Subarrays
- //-----------------------------------------------------------------------
- /**
- * <p>Produces a new array containing the elements between
- * the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * <p>The component type of the subarray is always the same as
- * that of the input array. Thus, if the input is an array of type
- * {@code Date}, the following usage is envisaged:</p>
- *
- * <pre>
- * Date[] someDates = (Date[])ArrayUtils.subarray(allDates, 2, 5);
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static <T> T[] subarray(T[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- Class<?> type = array.getClass().getComponentType();
- if (newSize <= 0) {
- @SuppressWarnings("unchecked") // OK, because array is of type T
- final T[] emptyArray = (T[]) Array.newInstance(type, 0);
- return emptyArray;
- }
- @SuppressWarnings("unchecked") // OK, because array is of type T
- T[] subarray = (T[]) Array.newInstance(type, newSize);
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code long} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static long[] subarray(long[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_LONG_ARRAY;
- }
-
- long[] subarray = new long[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code int} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static int[] subarray(int[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_INT_ARRAY;
- }
-
- int[] subarray = new int[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code short} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static short[] subarray(short[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_SHORT_ARRAY;
- }
-
- short[] subarray = new short[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code char} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static char[] subarray(char[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_CHAR_ARRAY;
- }
-
- char[] subarray = new char[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code byte} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_BYTE_ARRAY;
- }
-
- byte[] subarray = new byte[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code double} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static double[] subarray(double[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_DOUBLE_ARRAY;
- }
-
- double[] subarray = new double[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code float} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static float[] subarray(float[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_FLOAT_ARRAY;
- }
-
- float[] subarray = new float[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- /**
- * <p>Produces a new {@code boolean} array containing the elements
- * between the start and end indices.</p>
- *
- * <p>The start index is inclusive, the end index exclusive.
- * Null array input produces null output.</p>
- *
- * @param array the array
- * @param startIndexInclusive the starting index. Undervalue (<0)
- * is promoted to 0, overvalue (>array.length) results
- * in an empty array.
- * @param endIndexExclusive elements up to endIndex-1 are present in the
- * returned subarray. Undervalue (< startIndex) produces
- * empty array, overvalue (>array.length) is demoted to
- * array length.
- * @return a new array containing the elements between
- * the start and end indices.
- * @since 2.1
- */
- public static boolean[] subarray(boolean[] array, int startIndexInclusive, int endIndexExclusive) {
- if (array == null) {
- return null;
- }
- if (startIndexInclusive < 0) {
- startIndexInclusive = 0;
- }
- if (endIndexExclusive > array.length) {
- endIndexExclusive = array.length;
- }
- int newSize = endIndexExclusive - startIndexInclusive;
- if (newSize <= 0) {
- return EMPTY_BOOLEAN_ARRAY;
- }
-
- boolean[] subarray = new boolean[newSize];
- System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
- return subarray;
- }
-
- // Is same length
- //-----------------------------------------------------------------------
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.
- *
- * <p>Any multi-dimensional aspects of the arrays are ignored.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(Object[] array1, Object[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(long[] array1, long[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(int[] array1, int[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(short[] array1, short[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(char[] array1, char[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(byte[] array1, byte[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(double[] array1, double[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(float[] array1, float[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- /**
- * <p>Checks whether two arrays are the same length, treating
- * {@code null} arrays as length {@code 0}.</p>
- *
- * @param array1 the first array, may be {@code null}
- * @param array2 the second array, may be {@code null}
- * @return {@code true} if length of arrays matches, treating
- * {@code null} as an empty array
- */
- public static boolean isSameLength(boolean[] array1, boolean[] array2) {
- if ((array1 == null && array2 != null && array2.length > 0) ||
- (array2 == null && array1 != null && array1.length > 0) ||
- (array1 != null && array2 != null && array1.length != array2.length)) {
- return false;
- }
- return true;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns the length of the specified array.
- * This method can deal with {@code Object} arrays and with primitive arrays.</p>
- *
- * <p>If the input array is {@code null}, {@code 0} is returned.</p>
- *
- * <pre>
- * ArrayUtils.getLength(null) = 0
- * ArrayUtils.getLength([]) = 0
- * ArrayUtils.getLength([null]) = 1
- * ArrayUtils.getLength([true, false]) = 2
- * ArrayUtils.getLength([1, 2, 3]) = 3
- * ArrayUtils.getLength(["a", "b", "c"]) = 3
- * </pre>
- *
- * @param array the array to retrieve the length from, may be null
- * @return The length of the array, or {@code 0} if the array is {@code null}
- * @throws IllegalArgumentException if the object arguement is not an array.
- * @since 2.1
- */
- public static int getLength(Object array) {
- if (array == null) {
- return 0;
- }
- return Array.getLength(array);
- }
-
- /**
- * <p>Checks whether two arrays are the same type taking into account
- * multi-dimensional arrays.</p>
- *
- * @param array1 the first array, must not be {@code null}
- * @param array2 the second array, must not be {@code null}
- * @return {@code true} if type of arrays matches
- * @throws IllegalArgumentException if either array is {@code null}
- */
- public static boolean isSameType(Object array1, Object array2) {
- if (array1 == null || array2 == null) {
- throw new IllegalArgumentException("The Array must not be null");
- }
- return array1.getClass().getName().equals(array2.getClass().getName());
- }
-
- // Reverse
- //-----------------------------------------------------------------------
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>There is no special handling for multi-dimensional arrays.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(Object[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- Object tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(long[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- long tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(int[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- int tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(short[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- short tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(char[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- char tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(byte[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- byte tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(double[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- double tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(float[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- float tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- /**
- * <p>Reverses the order of the given array.</p>
- *
- * <p>This method does nothing for a {@code null} input array.</p>
- *
- * @param array the array to reverse, may be {@code null}
- */
- public static void reverse(boolean[] array) {
- if (array == null) {
- return;
- }
- int i = 0;
- int j = array.length - 1;
- boolean tmp;
- while (j > i) {
- tmp = array[j];
- array[j] = array[i];
- array[i] = tmp;
- j--;
- i++;
- }
- }
-
- // IndexOf search
- // ----------------------------------------------------------------------
-
- // Object IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given object in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param objectToFind the object to find, may be {@code null}
- * @return the index of the object within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(Object[] array, Object objectToFind) {
- return indexOf(array, objectToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given object in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param objectToFind the object to find, may be {@code null}
- * @param startIndex the index to start searching at
- * @return the index of the object within the array starting at the index,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(Object[] array, Object objectToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- if (objectToFind == null) {
- for (int i = startIndex; i < array.length; i++) {
- if (array[i] == null) {
- return i;
- }
- }
- } else if (array.getClass().getComponentType().isInstance(objectToFind)) {
- for (int i = startIndex; i < array.length; i++) {
- if (objectToFind.equals(array[i])) {
- return i;
- }
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given object within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param objectToFind the object to find, may be {@code null}
- * @return the last index of the object within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(Object[] array, Object objectToFind) {
- return lastIndexOf(array, objectToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given object in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than
- * the array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param objectToFind the object to find, may be {@code null}
- * @param startIndex the start index to travers backwards from
- * @return the last index of the object within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(Object[] array, Object objectToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- if (objectToFind == null) {
- for (int i = startIndex; i >= 0; i--) {
- if (array[i] == null) {
- return i;
- }
- }
- } else if (array.getClass().getComponentType().isInstance(objectToFind)) {
- for (int i = startIndex; i >= 0; i--) {
- if (objectToFind.equals(array[i])) {
- return i;
- }
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the object is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param objectToFind the object to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(Object[] array, Object objectToFind) {
- return indexOf(array, objectToFind) != INDEX_NOT_FOUND;
- }
-
- // long IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(long[] array, long valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(long[] array, long valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(long[] array, long valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(long[] array, long valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(long[] array, long valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // int IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(int[] array, int valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(int[] array, int valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(int[] array, int valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(int[] array, int valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(int[] array, int valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // short IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(short[] array, short valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(short[] array, short valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(short[] array, short valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(short[] array, short valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(short[] array, short valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // char IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- * @since 2.1
- */
- public static int indexOf(char[] array, char valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- * @since 2.1
- */
- public static int indexOf(char[] array, char valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- * @since 2.1
- */
- public static int lastIndexOf(char[] array, char valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- * @since 2.1
- */
- public static int lastIndexOf(char[] array, char valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- * @since 2.1
- */
- public static boolean contains(char[] array, char valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // byte IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(byte[] array, byte valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(byte[] array, byte valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(byte[] array, byte valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(byte[] array, byte valueToFind, int startIndex) {
- if (array == null) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(byte[] array, byte valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // double IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(double[] array, double valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value within a given tolerance in the array.
- * This method will return the index of the first value which falls between the region
- * defined by valueToFind - tolerance and valueToFind + tolerance.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param tolerance tolerance of the search
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(double[] array, double valueToFind, double tolerance) {
- return indexOf(array, valueToFind, 0, tolerance);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(double[] array, double valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.
- * This method will return the index of the first value which falls between the region
- * defined by valueToFind - tolerance and valueToFind + tolerance.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @param tolerance tolerance of the search
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(double[] array, double valueToFind, int startIndex, double tolerance) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- double min = valueToFind - tolerance;
- double max = valueToFind + tolerance;
- for (int i = startIndex; i < array.length; i++) {
- if (array[i] >= min && array[i] <= max) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(double[] array, double valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value within a given tolerance in the array.
- * This method will return the index of the last value which falls between the region
- * defined by valueToFind - tolerance and valueToFind + tolerance.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param tolerance tolerance of the search
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(double[] array, double valueToFind, double tolerance) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(double[] array, double valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.
- * This method will return the index of the last value which falls between the region
- * defined by valueToFind - tolerance and valueToFind + tolerance.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @param tolerance search for value within plus/minus this amount
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(double[] array, double valueToFind, int startIndex, double tolerance) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- double min = valueToFind - tolerance;
- double max = valueToFind + tolerance;
- for (int i = startIndex; i >= 0; i--) {
- if (array[i] >= min && array[i] <= max) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(double[] array, double valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if a value falling within the given tolerance is in the
- * given array. If the array contains a value within the inclusive range
- * defined by (value - tolerance) to (value + tolerance).</p>
- *
- * <p>The method returns {@code false} if a {@code null} array
- * is passed in.</p>
- *
- * @param array the array to search
- * @param valueToFind the value to find
- * @param tolerance the array contains the tolerance of the search
- * @return true if value falling within tolerance is in array
- */
- public static boolean contains(double[] array, double valueToFind, double tolerance) {
- return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND;
- }
-
- // float IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(float[] array, float valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(float[] array, float valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(float[] array, float valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the
- * array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(float[] array, float valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(float[] array, float valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // boolean IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the index of the given value in the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int indexOf(boolean[] array, boolean valueToFind) {
- return indexOf(array, valueToFind, 0);
- }
-
- /**
- * <p>Finds the index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex is treated as zero. A startIndex larger than the array
- * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
- *
- * @param array the array to search through for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the index to start searching at
- * @return the index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null}
- * array input
- */
- public static int indexOf(boolean[] array, boolean valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- startIndex = 0;
- }
- for (int i = startIndex; i < array.length; i++) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Finds the last index of the given value within the array.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) if
- * {@code null} array input.</p>
- *
- * @param array the array to travers backwords looking for the object, may be {@code null}
- * @param valueToFind the object to find
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(boolean[] array, boolean valueToFind) {
- return lastIndexOf(array, valueToFind, Integer.MAX_VALUE);
- }
-
- /**
- * <p>Finds the last index of the given value in the array starting at the given index.</p>
- *
- * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
- *
- * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than
- * the array length will search from the end of the array.</p>
- *
- * @param array the array to traverse for looking for the object, may be {@code null}
- * @param valueToFind the value to find
- * @param startIndex the start index to travers backwards from
- * @return the last index of the value within the array,
- * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
- */
- public static int lastIndexOf(boolean[] array, boolean valueToFind, int startIndex) {
- if (ArrayUtils.isEmpty(array)) {
- return INDEX_NOT_FOUND;
- }
- if (startIndex < 0) {
- return INDEX_NOT_FOUND;
- } else if (startIndex >= array.length) {
- startIndex = array.length - 1;
- }
- for (int i = startIndex; i >= 0; i--) {
- if (valueToFind == array[i]) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the value is in the given array.</p>
- *
- * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
- *
- * @param array the array to search through
- * @param valueToFind the value to find
- * @return {@code true} if the array contains the object
- */
- public static boolean contains(boolean[] array, boolean valueToFind) {
- return indexOf(array, valueToFind) != INDEX_NOT_FOUND;
- }
-
- // Primitive/Object array converters
- // ----------------------------------------------------------------------
-
- // Character array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Characters to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Character} array, may be {@code null}
- * @return a {@code char} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static char[] toPrimitive(Character[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_CHAR_ARRAY;
- }
- final char[] result = new char[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].charValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Character to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Character} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code char} array, {@code null} if null array input
- */
- public static char[] toPrimitive(Character[] array, char valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_CHAR_ARRAY;
- }
- final char[] result = new char[array.length];
- for (int i = 0; i < array.length; i++) {
- Character b = array[i];
- result[i] = (b == null ? valueForNull : b.charValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive chars to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code char} array
- * @return a {@code Character} array, {@code null} if null array input
- */
- public static Character[] toObject(char[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_CHARACTER_OBJECT_ARRAY;
- }
- final Character[] result = new Character[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Character.valueOf(array[i]);
- }
- return result;
- }
-
- // Long array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Longs to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Long} array, may be {@code null}
- * @return a {@code long} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static long[] toPrimitive(Long[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_LONG_ARRAY;
- }
- final long[] result = new long[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].longValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Long to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Long} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code long} array, {@code null} if null array input
- */
- public static long[] toPrimitive(Long[] array, long valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_LONG_ARRAY;
- }
- final long[] result = new long[array.length];
- for (int i = 0; i < array.length; i++) {
- Long b = array[i];
- result[i] = (b == null ? valueForNull : b.longValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive longs to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code long} array
- * @return a {@code Long} array, {@code null} if null array input
- */
- public static Long[] toObject(long[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_LONG_OBJECT_ARRAY;
- }
- final Long[] result = new Long[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Long.valueOf(array[i]);
- }
- return result;
- }
-
- // Int array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Integers to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Integer} array, may be {@code null}
- * @return an {@code int} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static int[] toPrimitive(Integer[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_INT_ARRAY;
- }
- final int[] result = new int[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].intValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Integer to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Integer} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return an {@code int} array, {@code null} if null array input
- */
- public static int[] toPrimitive(Integer[] array, int valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_INT_ARRAY;
- }
- final int[] result = new int[array.length];
- for (int i = 0; i < array.length; i++) {
- Integer b = array[i];
- result[i] = (b == null ? valueForNull : b.intValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive ints to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array an {@code int} array
- * @return an {@code Integer} array, {@code null} if null array input
- */
- public static Integer[] toObject(int[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_INTEGER_OBJECT_ARRAY;
- }
- final Integer[] result = new Integer[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Integer.valueOf(array[i]);
- }
- return result;
- }
-
- // Short array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Shorts to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Short} array, may be {@code null}
- * @return a {@code byte} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static short[] toPrimitive(Short[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_SHORT_ARRAY;
- }
- final short[] result = new short[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].shortValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Short to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Short} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code byte} array, {@code null} if null array input
- */
- public static short[] toPrimitive(Short[] array, short valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_SHORT_ARRAY;
- }
- final short[] result = new short[array.length];
- for (int i = 0; i < array.length; i++) {
- Short b = array[i];
- result[i] = (b == null ? valueForNull : b.shortValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive shorts to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code short} array
- * @return a {@code Short} array, {@code null} if null array input
- */
- public static Short[] toObject(short[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_SHORT_OBJECT_ARRAY;
- }
- final Short[] result = new Short[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Short.valueOf(array[i]);
- }
- return result;
- }
-
- // Byte array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Bytes to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Byte} array, may be {@code null}
- * @return a {@code byte} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static byte[] toPrimitive(Byte[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BYTE_ARRAY;
- }
- final byte[] result = new byte[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].byteValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Bytes to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Byte} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code byte} array, {@code null} if null array input
- */
- public static byte[] toPrimitive(Byte[] array, byte valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BYTE_ARRAY;
- }
- final byte[] result = new byte[array.length];
- for (int i = 0; i < array.length; i++) {
- Byte b = array[i];
- result[i] = (b == null ? valueForNull : b.byteValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive bytes to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code byte} array
- * @return a {@code Byte} array, {@code null} if null array input
- */
- public static Byte[] toObject(byte[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BYTE_OBJECT_ARRAY;
- }
- final Byte[] result = new Byte[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Byte.valueOf(array[i]);
- }
- return result;
- }
-
- // Double array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Doubles to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Double} array, may be {@code null}
- * @return a {@code double} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static double[] toPrimitive(Double[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_DOUBLE_ARRAY;
- }
- final double[] result = new double[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].doubleValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Doubles to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Double} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code double} array, {@code null} if null array input
- */
- public static double[] toPrimitive(Double[] array, double valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_DOUBLE_ARRAY;
- }
- final double[] result = new double[array.length];
- for (int i = 0; i < array.length; i++) {
- Double b = array[i];
- result[i] = (b == null ? valueForNull : b.doubleValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive doubles to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code double} array
- * @return a {@code Double} array, {@code null} if null array input
- */
- public static Double[] toObject(double[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_DOUBLE_OBJECT_ARRAY;
- }
- final Double[] result = new Double[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Double.valueOf(array[i]);
- }
- return result;
- }
-
- // Float array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Floats to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Float} array, may be {@code null}
- * @return a {@code float} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static float[] toPrimitive(Float[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_FLOAT_ARRAY;
- }
- final float[] result = new float[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].floatValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Floats to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Float} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code float} array, {@code null} if null array input
- */
- public static float[] toPrimitive(Float[] array, float valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_FLOAT_ARRAY;
- }
- final float[] result = new float[array.length];
- for (int i = 0; i < array.length; i++) {
- Float b = array[i];
- result[i] = (b == null ? valueForNull : b.floatValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive floats to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code float} array
- * @return a {@code Float} array, {@code null} if null array input
- */
- public static Float[] toObject(float[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_FLOAT_OBJECT_ARRAY;
- }
- final Float[] result = new Float[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = Float.valueOf(array[i]);
- }
- return result;
- }
-
- // Boolean array converters
- // ----------------------------------------------------------------------
- /**
- * <p>Converts an array of object Booleans to primitives.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Boolean} array, may be {@code null}
- * @return a {@code boolean} array, {@code null} if null array input
- * @throws NullPointerException if array content is {@code null}
- */
- public static boolean[] toPrimitive(Boolean[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BOOLEAN_ARRAY;
- }
- final boolean[] result = new boolean[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = array[i].booleanValue();
- }
- return result;
- }
-
- /**
- * <p>Converts an array of object Booleans to primitives handling {@code null}.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code Boolean} array, may be {@code null}
- * @param valueForNull the value to insert if {@code null} found
- * @return a {@code boolean} array, {@code null} if null array input
- */
- public static boolean[] toPrimitive(Boolean[] array, boolean valueForNull) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BOOLEAN_ARRAY;
- }
- final boolean[] result = new boolean[array.length];
- for (int i = 0; i < array.length; i++) {
- Boolean b = array[i];
- result[i] = (b == null ? valueForNull : b.booleanValue());
- }
- return result;
- }
-
- /**
- * <p>Converts an array of primitive booleans to objects.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array a {@code boolean} array
- * @return a {@code Boolean} array, {@code null} if null array input
- */
- public static Boolean[] toObject(boolean[] array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return EMPTY_BOOLEAN_OBJECT_ARRAY;
- }
- final Boolean[] result = new Boolean[array.length];
- for (int i = 0; i < array.length; i++) {
- result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE);
- }
- return result;
- }
-
- // ----------------------------------------------------------------------
- /**
- * <p>Checks if an array of Objects is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(Object[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive longs is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(long[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive ints is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(int[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive shorts is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(short[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive chars is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(char[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive bytes is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(byte[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive doubles is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(double[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive floats is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(float[] array) {
- return array == null || array.length == 0;
- }
-
- /**
- * <p>Checks if an array of primitive booleans is empty or {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is empty or {@code null}
- * @since 2.1
- */
- public static boolean isEmpty(boolean[] array) {
- return array == null || array.length == 0;
- }
-
- // ----------------------------------------------------------------------
- /**
- * <p>Checks if an array of Objects is not empty or not {@code null}.</p>
- *
- * @param <T> the component type of the array
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static <T> boolean isNotEmpty(T[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive longs is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(long[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive ints is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(int[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive shorts is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(short[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive chars is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(char[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive bytes is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(byte[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive doubles is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(double[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive floats is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(float[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Checks if an array of primitive booleans is not empty or not {@code null}.</p>
- *
- * @param array the array to test
- * @return {@code true} if the array is not empty or not {@code null}
- * @since 2.5
- */
- public static boolean isNotEmpty(boolean[] array) {
- return (array != null && array.length != 0);
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(null, null) = null
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * ArrayUtils.addAll([null], [null]) = [null, null]
- * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array1 the first array whose elements are added to the new array, may be {@code null}
- * @param array2 the second array whose elements are added to the new array, may be {@code null}
- * @return The new array, {@code null} if both arrays are {@code null}.
- * The type of the new array is the type of the first array,
- * unless the first array is null, in which case the type is the same as the second array.
- * @since 2.1
- * @throws IllegalArgumentException if the array types are incompatible
- */
- public static <T> T[] addAll(T[] array1, T... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- final Class<?> type1 = array1.getClass().getComponentType();
- @SuppressWarnings("unchecked") // OK, because array is of type T
- T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length);
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- try {
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- } catch (ArrayStoreException ase) {
- // Check if problem was due to incompatible types
- /*
- * We do this here, rather than before the copy because:
- * - it would be a wasted check most of the time
- * - safer, in case check turns out to be too strict
- */
- final Class<?> type2 = array2.getClass().getComponentType();
- if (!type1.isAssignableFrom(type2)){
- throw new IllegalArgumentException("Cannot store "+type2.getName()+" in an array of "
- +type1.getName(), ase);
- }
- throw ase; // No, so rethrow original
- }
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new boolean[] array.
- * @since 2.1
- */
- public static boolean[] addAll(boolean[] array1, boolean... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- boolean[] joinedArray = new boolean[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new char[] array.
- * @since 2.1
- */
- public static char[] addAll(char[] array1, char... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- char[] joinedArray = new char[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new byte[] array.
- * @since 2.1
- */
- public static byte[] addAll(byte[] array1, byte... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- byte[] joinedArray = new byte[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new short[] array.
- * @since 2.1
- */
- public static short[] addAll(short[] array1, short... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- short[] joinedArray = new short[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new int[] array.
- * @since 2.1
- */
- public static int[] addAll(int[] array1, int... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- int[] joinedArray = new int[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new long[] array.
- * @since 2.1
- */
- public static long[] addAll(long[] array1, long... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- long[] joinedArray = new long[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new float[] array.
- * @since 2.1
- */
- public static float[] addAll(float[] array1, float... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- float[] joinedArray = new float[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Adds all the elements of the given arrays into a new array.</p>
- * <p>The new array contains all of the element of {@code array1} followed
- * by all of the elements {@code array2}. When an array is returned, it is always
- * a new array.</p>
- *
- * <pre>
- * ArrayUtils.addAll(array1, null) = cloned copy of array1
- * ArrayUtils.addAll(null, array2) = cloned copy of array2
- * ArrayUtils.addAll([], []) = []
- * </pre>
- *
- * @param array1 the first array whose elements are added to the new array.
- * @param array2 the second array whose elements are added to the new array.
- * @return The new double[] array.
- * @since 2.1
- */
- public static double[] addAll(double[] array1, double... array2) {
- if (array1 == null) {
- return clone(array2);
- } else if (array2 == null) {
- return clone(array1);
- }
- double[] joinedArray = new double[array1.length + array2.length];
- System.arraycopy(array1, 0, joinedArray, 0, array1.length);
- System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
- return joinedArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element, unless the element itself is null,
- * in which case the return type is Object[]</p>
- *
- * <pre>
- * ArrayUtils.add(null, null) = [null]
- * ArrayUtils.add(null, "a") = ["a"]
- * ArrayUtils.add(["a"], null) = ["a", null]
- * ArrayUtils.add(["a"], "b") = ["a", "b"]
- * ArrayUtils.add(["a", "b"], "c") = ["a", "b", "c"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to "add" the element to, may be {@code null}
- * @param element the object to add, may be {@code null}
- * @return A new array containing the existing elements plus the new element
- * The returned array type will be that of the input array (unless null),
- * in which case it will have the same type as the element.
- * If both are null, an IllegalArgumentException is thrown
- * @since 2.1
- * @throws IllegalArgumentException if both arguments are null
- */
- public static <T> T[] add(T[] array, T element) {
- Class<?> type;
- if (array != null){
- type = array.getClass();
- } else if (element != null) {
- type = element.getClass();
- } else {
- throw new IllegalArgumentException("Arguments cannot both be null");
- }
- @SuppressWarnings("unchecked") // type must be T
- T[] newArray = (T[]) copyArrayGrow1(array, type);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, true) = [true]
- * ArrayUtils.add([true], false) = [true, false]
- * ArrayUtils.add([true, false], true) = [true, false, true]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static boolean[] add(boolean[] array, boolean element) {
- boolean[] newArray = (boolean[])copyArrayGrow1(array, Boolean.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static byte[] add(byte[] array, byte element) {
- byte[] newArray = (byte[])copyArrayGrow1(array, Byte.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, '0') = ['0']
- * ArrayUtils.add(['1'], '0') = ['1', '0']
- * ArrayUtils.add(['1', '0'], '1') = ['1', '0', '1']
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static char[] add(char[] array, char element) {
- char[] newArray = (char[])copyArrayGrow1(array, Character.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static double[] add(double[] array, double element) {
- double[] newArray = (double[])copyArrayGrow1(array, Double.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static float[] add(float[] array, float element) {
- float[] newArray = (float[])copyArrayGrow1(array, Float.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static int[] add(int[] array, int element) {
- int[] newArray = (int[])copyArrayGrow1(array, Integer.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static long[] add(long[] array, long element) {
- long[] newArray = (long[])copyArrayGrow1(array, Long.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * <p>Copies the given array and adds the given element at the end of the new array.</p>
- *
- * <p>The new array contains the same elements of the input
- * array plus the given element in the last position. The component type of
- * the new array is the same as that of the input array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0) = [0]
- * ArrayUtils.add([1], 0) = [1, 0]
- * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
- * </pre>
- *
- * @param array the array to copy and add the element to, may be {@code null}
- * @param element the object to add at the last index of the new array
- * @return A new array containing the existing elements plus the new element
- * @since 2.1
- */
- public static short[] add(short[] array, short element) {
- short[] newArray = (short[])copyArrayGrow1(array, Short.TYPE);
- newArray[newArray.length - 1] = element;
- return newArray;
- }
-
- /**
- * Returns a copy of the given array of size 1 greater than the argument.
- * The last value of the array is left to the default value.
- *
- * @param array The array to copy, must not be {@code null}.
- * @param newArrayComponentType If {@code array} is {@code null}, create a
- * size 1 array of this type.
- * @return A new copy of the array of size 1 greater than the input.
- */
- private static Object copyArrayGrow1(Object array, Class<?> newArrayComponentType) {
- if (array != null) {
- int arrayLength = Array.getLength(array);
- Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1);
- System.arraycopy(array, 0, newArray, 0, arrayLength);
- return newArray;
- }
- return Array.newInstance(newArrayComponentType, 1);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0, null) = [null]
- * ArrayUtils.add(null, 0, "a") = ["a"]
- * ArrayUtils.add(["a"], 1, null) = ["a", null]
- * ArrayUtils.add(["a"], 1, "b") = ["a", "b"]
- * ArrayUtils.add(["a", "b"], 3, "c") = ["a", "b", "c"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- * @throws IllegalArgumentException if both array and element are null
- */
- public static <T> T[] add(T[] array, int index, T element) {
- Class<?> clss = null;
- if (array != null) {
- clss = array.getClass().getComponentType();
- } else if (element != null) {
- clss = element.getClass();
- } else {
- throw new IllegalArgumentException("Array and element cannot both be null");
- }
- @SuppressWarnings("unchecked") // the add method creates an array of type clss, which is type T
- final T[] newArray = (T[]) add(array, index, element, clss);
- return newArray;
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0, true) = [true]
- * ArrayUtils.add([true], 0, false) = [false, true]
- * ArrayUtils.add([false], 1, true) = [false, true]
- * ArrayUtils.add([true, false], 1, true) = [true, true, false]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static boolean[] add(boolean[] array, int index, boolean element) {
- return (boolean[]) add(array, index, Boolean.valueOf(element), Boolean.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add(null, 0, 'a') = ['a']
- * ArrayUtils.add(['a'], 0, 'b') = ['b', 'a']
- * ArrayUtils.add(['a', 'b'], 0, 'c') = ['c', 'a', 'b']
- * ArrayUtils.add(['a', 'b'], 1, 'k') = ['a', 'k', 'b']
- * ArrayUtils.add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c']
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static char[] add(char[] array, int index, char element) {
- return (char[]) add(array, index, Character.valueOf(element), Character.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1], 0, 2) = [2, 1]
- * ArrayUtils.add([2, 6], 2, 3) = [2, 6, 3]
- * ArrayUtils.add([2, 6], 0, 1) = [1, 2, 6]
- * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static byte[] add(byte[] array, int index, byte element) {
- return (byte[]) add(array, index, Byte.valueOf(element), Byte.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1], 0, 2) = [2, 1]
- * ArrayUtils.add([2, 6], 2, 10) = [2, 6, 10]
- * ArrayUtils.add([2, 6], 0, -4) = [-4, 2, 6]
- * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static short[] add(short[] array, int index, short element) {
- return (short[]) add(array, index, Short.valueOf(element), Short.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1], 0, 2) = [2, 1]
- * ArrayUtils.add([2, 6], 2, 10) = [2, 6, 10]
- * ArrayUtils.add([2, 6], 0, -4) = [-4, 2, 6]
- * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static int[] add(int[] array, int index, int element) {
- return (int[]) add(array, index, Integer.valueOf(element), Integer.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1L], 0, 2L) = [2L, 1L]
- * ArrayUtils.add([2L, 6L], 2, 10L) = [2L, 6L, 10L]
- * ArrayUtils.add([2L, 6L], 0, -4L) = [-4L, 2L, 6L]
- * ArrayUtils.add([2L, 6L, 3L], 2, 1L) = [2L, 6L, 1L, 3L]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static long[] add(long[] array, int index, long element) {
- return (long[]) add(array, index, Long.valueOf(element), Long.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1.1f], 0, 2.2f) = [2.2f, 1.1f]
- * ArrayUtils.add([2.3f, 6.4f], 2, 10.5f) = [2.3f, 6.4f, 10.5f]
- * ArrayUtils.add([2.6f, 6.7f], 0, -4.8f) = [-4.8f, 2.6f, 6.7f]
- * ArrayUtils.add([2.9f, 6.0f, 0.3f], 2, 1.0f) = [2.9f, 6.0f, 1.0f, 0.3f]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static float[] add(float[] array, int index, float element) {
- return (float[]) add(array, index, Float.valueOf(element), Float.TYPE);
- }
-
- /**
- * <p>Inserts the specified element at the specified position in the array.
- * Shifts the element currently at that position (if any) and any subsequent
- * elements to the right (adds one to their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array plus the given element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, a new one element array is returned
- * whose component type is the same as the element.</p>
- *
- * <pre>
- * ArrayUtils.add([1.1], 0, 2.2) = [2.2, 1.1]
- * ArrayUtils.add([2.3, 6.4], 2, 10.5) = [2.3, 6.4, 10.5]
- * ArrayUtils.add([2.6, 6.7], 0, -4.8) = [-4.8, 2.6, 6.7]
- * ArrayUtils.add([2.9, 6.0, 0.3], 2, 1.0) = [2.9, 6.0, 1.0, 0.3]
- * </pre>
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @return A new array containing the existing elements and the new element
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index > array.length).
- */
- public static double[] add(double[] array, int index, double element) {
- return (double[]) add(array, index, Double.valueOf(element), Double.TYPE);
- }
-
- /**
- * Underlying implementation of add(array, index, element) methods.
- * The last parameter is the class, which may not equal element.getClass
- * for primitives.
- *
- * @param array the array to add the element to, may be {@code null}
- * @param index the position of the new object
- * @param element the object to add
- * @param clss the type of the element being added
- * @return A new array containing the existing elements and the new element
- */
- private static Object add(Object array, int index, Object element, Class<?> clss) {
- if (array == null) {
- if (index != 0) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Length: 0");
- }
- Object joinedArray = Array.newInstance(clss, 1);
- Array.set(joinedArray, 0, element);
- return joinedArray;
- }
- int length = Array.getLength(array);
- if (index > length || index < 0) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length);
- }
- Object result = Array.newInstance(clss, length + 1);
- System.arraycopy(array, 0, result, 0, index);
- Array.set(result, index, element);
- if (index < length) {
- System.arraycopy(array, index, result, index + 1, length - index);
- }
- return result;
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove(["a"], 0) = []
- * ArrayUtils.remove(["a", "b"], 0) = ["b"]
- * ArrayUtils.remove(["a", "b"], 1) = ["a"]
- * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- @SuppressWarnings("unchecked") // remove() always creates an array of the same type as its input
- public static <T> T[] remove(T[] array, int index) {
- return (T[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, "a") = null
- * ArrayUtils.removeElement([], "a") = []
- * ArrayUtils.removeElement(["a"], "b") = ["a"]
- * ArrayUtils.removeElement(["a", "b"], "a") = ["b"]
- * ArrayUtils.removeElement(["a", "b", "a"], "a") = ["b", "a"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static <T> T[] removeElement(T[] array, Object element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([true], 0) = []
- * ArrayUtils.remove([true, false], 0) = [false]
- * ArrayUtils.remove([true, false], 1) = [true]
- * ArrayUtils.remove([true, true, false], 1) = [true, false]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static boolean[] remove(boolean[] array, int index) {
- return (boolean[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, true) = null
- * ArrayUtils.removeElement([], true) = []
- * ArrayUtils.removeElement([true], false) = [true]
- * ArrayUtils.removeElement([true, false], false) = [true]
- * ArrayUtils.removeElement([true, false, true], true) = [false, true]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static boolean[] removeElement(boolean[] array, boolean element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1], 0) = []
- * ArrayUtils.remove([1, 0], 0) = [0]
- * ArrayUtils.remove([1, 0], 1) = [1]
- * ArrayUtils.remove([1, 0, 1], 1) = [1, 1]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static byte[] remove(byte[] array, int index) {
- return (byte[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1) = null
- * ArrayUtils.removeElement([], 1) = []
- * ArrayUtils.removeElement([1], 0) = [1]
- * ArrayUtils.removeElement([1, 0], 0) = [1]
- * ArrayUtils.removeElement([1, 0, 1], 1) = [0, 1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static byte[] removeElement(byte[] array, byte element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove(['a'], 0) = []
- * ArrayUtils.remove(['a', 'b'], 0) = ['b']
- * ArrayUtils.remove(['a', 'b'], 1) = ['a']
- * ArrayUtils.remove(['a', 'b', 'c'], 1) = ['a', 'c']
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static char[] remove(char[] array, int index) {
- return (char[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 'a') = null
- * ArrayUtils.removeElement([], 'a') = []
- * ArrayUtils.removeElement(['a'], 'b') = ['a']
- * ArrayUtils.removeElement(['a', 'b'], 'a') = ['b']
- * ArrayUtils.removeElement(['a', 'b', 'a'], 'a') = ['b', 'a']
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static char[] removeElement(char[] array, char element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1.1], 0) = []
- * ArrayUtils.remove([2.5, 6.0], 0) = [6.0]
- * ArrayUtils.remove([2.5, 6.0], 1) = [2.5]
- * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static double[] remove(double[] array, int index) {
- return (double[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1.1) = null
- * ArrayUtils.removeElement([], 1.1) = []
- * ArrayUtils.removeElement([1.1], 1.2) = [1.1]
- * ArrayUtils.removeElement([1.1, 2.3], 1.1) = [2.3]
- * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static double[] removeElement(double[] array, double element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1.1], 0) = []
- * ArrayUtils.remove([2.5, 6.0], 0) = [6.0]
- * ArrayUtils.remove([2.5, 6.0], 1) = [2.5]
- * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static float[] remove(float[] array, int index) {
- return (float[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1.1) = null
- * ArrayUtils.removeElement([], 1.1) = []
- * ArrayUtils.removeElement([1.1], 1.2) = [1.1]
- * ArrayUtils.removeElement([1.1, 2.3], 1.1) = [2.3]
- * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static float[] removeElement(float[] array, float element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1], 0) = []
- * ArrayUtils.remove([2, 6], 0) = [6]
- * ArrayUtils.remove([2, 6], 1) = [2]
- * ArrayUtils.remove([2, 6, 3], 1) = [2, 3]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static int[] remove(int[] array, int index) {
- return (int[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1) = null
- * ArrayUtils.removeElement([], 1) = []
- * ArrayUtils.removeElement([1], 2) = [1]
- * ArrayUtils.removeElement([1, 3], 1) = [3]
- * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static int[] removeElement(int[] array, int element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1], 0) = []
- * ArrayUtils.remove([2, 6], 0) = [6]
- * ArrayUtils.remove([2, 6], 1) = [2]
- * ArrayUtils.remove([2, 6, 3], 1) = [2, 3]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static long[] remove(long[] array, int index) {
- return (long[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1) = null
- * ArrayUtils.removeElement([], 1) = []
- * ArrayUtils.removeElement([1], 2) = [1]
- * ArrayUtils.removeElement([1, 3], 1) = [3]
- * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static long[] removeElement(long[] array, long element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.remove([1], 0) = []
- * ArrayUtils.remove([2, 6], 0) = [6]
- * ArrayUtils.remove([2, 6], 1) = [2]
- * ArrayUtils.remove([2, 6, 3], 1) = [2, 3]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- public static short[] remove(short[] array, int index) {
- return (short[]) remove((Object) array, index);
- }
-
- /**
- * <p>Removes the first occurrence of the specified element from the
- * specified array. All subsequent elements are shifted to the left
- * (subtracts one from their indices). If the array doesn't contains
- * such an element, no elements are removed from the array.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the first occurrence of the specified element. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <pre>
- * ArrayUtils.removeElement(null, 1) = null
- * ArrayUtils.removeElement([], 1) = []
- * ArrayUtils.removeElement([1], 2) = [1]
- * ArrayUtils.removeElement([1, 3], 1) = [3]
- * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param element the element to be removed
- * @return A new array containing the existing elements except the first
- * occurrence of the specified element.
- * @since 2.1
- */
- public static short[] removeElement(short[] array, short element) {
- int index = indexOf(array, element);
- if (index == INDEX_NOT_FOUND) {
- return clone(array);
- }
- return remove(array, index);
- }
-
- /**
- * <p>Removes the element at the specified position from the specified array.
- * All subsequent elements are shifted to the left (subtracts one from
- * their indices).</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except the element on the specified position. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param index the position of the element to be removed
- * @return A new array containing the existing elements except the element
- * at the specified position.
- * @throws IndexOutOfBoundsException if the index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 2.1
- */
- private static Object remove(Object array, int index) {
- int length = getLength(array);
- if (index < 0 || index >= length) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length);
- }
-
- Object result = Array.newInstance(array.getClass().getComponentType(), length - 1);
- System.arraycopy(array, 0, result, 0, index);
- if (index < length - 1) {
- System.arraycopy(array, index + 1, result, index, length - index - 1);
- }
-
- return result;
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll(["a", "b", "c"], 0, 2) = ["b"]
- * ArrayUtils.removeAll(["a", "b", "c"], 1, 2) = ["a"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- @SuppressWarnings("unchecked")
- // removeAll() always creates an array of the same type as its input
- public static <T> T[] removeAll(T[] array, int... indices) {
- return (T[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, "a", "b") = null
- * ArrayUtils.removeElements([], "a", "b") = []
- * ArrayUtils.removeElements(["a"], "b", "c") = ["a"]
- * ArrayUtils.removeElements(["a", "b"], "a", "c") = ["b"]
- * ArrayUtils.removeElements(["a", "b", "a"], "a") = ["b", "a"]
- * ArrayUtils.removeElements(["a", "b", "a"], "a", "a") = ["b"]
- * </pre>
- *
- * @param <T> the component type of the array
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static <T> T[] removeElements(T[] array, T... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(values.length);
- for (T v : values) {
- MutableInt count = occurrences.get(v);
- if (count == null) {
- occurrences.put(v, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) {
- T v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v, found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static byte[] removeAll(byte[] array, int... indices) {
- return (byte[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static byte[] removeElements(byte[] array, byte... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Byte, MutableInt> occurrences = new HashMap<Byte, MutableInt>(values.length);
- for (byte v : values) {
- Byte boxed = Byte.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Byte, MutableInt> e : occurrences.entrySet()) {
- Byte v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.byteValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static short[] removeAll(short[] array, int... indices) {
- return (short[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static short[] removeElements(short[] array, short... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Short, MutableInt> occurrences = new HashMap<Short, MutableInt>(values.length);
- for (short v : values) {
- Short boxed = Short.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Short, MutableInt> e : occurrences.entrySet()) {
- Short v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.shortValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static int[] removeAll(int[] array, int... indices) {
- return (int[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static int[] removeElements(int[] array, int... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Integer, MutableInt> occurrences = new HashMap<Integer, MutableInt>(values.length);
- for (int v : values) {
- Integer boxed = Integer.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Integer, MutableInt> e : occurrences.entrySet()) {
- Integer v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.intValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static char[] removeAll(char[] array, int... indices) {
- return (char[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static char[] removeElements(char[] array, char... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Character, MutableInt> occurrences = new HashMap<Character, MutableInt>(values.length);
- for (char v : values) {
- Character boxed = Character.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Character, MutableInt> e : occurrences.entrySet()) {
- Character v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.charValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static long[] removeAll(long[] array, int... indices) {
- return (long[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static long[] removeElements(long[] array, long... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Long, MutableInt> occurrences = new HashMap<Long, MutableInt>(values.length);
- for (long v : values) {
- Long boxed = Long.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Long, MutableInt> e : occurrences.entrySet()) {
- Long v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.longValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static float[] removeAll(float[] array, int... indices) {
- return (float[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static float[] removeElements(float[] array, float... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Float, MutableInt> occurrences = new HashMap<Float, MutableInt>(values.length);
- for (float v : values) {
- Float boxed = Float.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Float, MutableInt> e : occurrences.entrySet()) {
- Float v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.floatValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([1], 0) = []
- * ArrayUtils.removeAll([2, 6], 0) = [6]
- * ArrayUtils.removeAll([2, 6], 0, 1) = []
- * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2]
- * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6]
- * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static double[] removeAll(double[] array, int... indices) {
- return (double[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, 1, 2) = null
- * ArrayUtils.removeElements([], 1, 2) = []
- * ArrayUtils.removeElements([1], 2, 3) = [1]
- * ArrayUtils.removeElements([1, 3], 1, 2) = [3]
- * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1]
- * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static double[] removeElements(double[] array, double... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Double, MutableInt> occurrences = new HashMap<Double, MutableInt>(values.length);
- for (double v : values) {
- Double boxed = Double.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Double, MutableInt> e : occurrences.entrySet()) {
- Double v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.doubleValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * <p>Removes the elements at the specified positions from the specified array.
- * All remaining elements are shifted to the left.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except those at the specified positions. The component
- * type of the returned array is always the same as that of the input
- * array.</p>
- *
- * <p>If the input array is {@code null}, an IndexOutOfBoundsException
- * will be thrown, because in that case no valid index can be specified.</p>
- *
- * <pre>
- * ArrayUtils.removeAll([true, false, true], 0, 2) = [false]
- * ArrayUtils.removeAll([true, false, true], 1, 2) = [true]
- * </pre>
- *
- * @param array the array to remove the element from, may not be {@code null}
- * @param indices the positions of the elements to be removed
- * @return A new array containing the existing elements except those
- * at the specified positions.
- * @throws IndexOutOfBoundsException if any index is out of range
- * (index < 0 || index >= array.length), or if the array is {@code null}.
- * @since 3.0.1
- */
- public static boolean[] removeAll(boolean[] array, int... indices) {
- return (boolean[]) removeAll((Object) array, clone(indices));
- }
-
- /**
- * <p>Removes occurrences of specified elements, in specified quantities,
- * from the specified array. All subsequent elements are shifted left.
- * For any element-to-be-removed specified in greater quantities than
- * contained in the original array, no change occurs beyond the
- * removal of the existing matching items.</p>
- *
- * <p>This method returns a new array with the same elements of the input
- * array except for the earliest-encountered occurrences of the specified
- * elements. The component type of the returned array is always the same
- * as that of the input array.</p>
- *
- * <pre>
- * ArrayUtils.removeElements(null, true, false) = null
- * ArrayUtils.removeElements([], true, false) = []
- * ArrayUtils.removeElements([true], false, false) = [true]
- * ArrayUtils.removeElements([true, false], true, true) = [false]
- * ArrayUtils.removeElements([true, false, true], true) = [false, true]
- * ArrayUtils.removeElements([true, false, true], true, true) = [false]
- * </pre>
- *
- * @param array the array to remove the element from, may be {@code null}
- * @param values the elements to be removed
- * @return A new array containing the existing elements except the
- * earliest-encountered occurrences of the specified elements.
- * @since 3.0.1
- */
- public static boolean[] removeElements(boolean[] array, boolean... values) {
- if (isEmpty(array) || isEmpty(values)) {
- return clone(array);
- }
- HashMap<Boolean, MutableInt> occurrences = new HashMap<Boolean, MutableInt>(values.length);
- for (boolean v : values) {
- Boolean boxed = Boolean.valueOf(v);
- MutableInt count = occurrences.get(boxed);
- if (count == null) {
- occurrences.put(boxed, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- HashSet<Integer> toRemove = new HashSet<Integer>();
- for (Map.Entry<Boolean, MutableInt> e : occurrences.entrySet()) {
- Boolean v = e.getKey();
- int found = 0;
- for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
- found = indexOf(array, v.booleanValue(), found);
- if (found < 0) {
- break;
- }
- toRemove.add(found++);
- }
- }
- return removeAll(array, extractIndices(toRemove));
- }
-
- /**
- * Removes multiple array elements specified by index.
- * @param array source
- * @param indices to remove, WILL BE SORTED--so only clones of user-owned arrays!
- * @return new array of same type minus elements specified by unique values of {@code indices}
- * @since 3.0.1
- */
- private static Object removeAll(Object array, int... indices) {
- int length = getLength(array);
- int diff = 0;
-
- if (isNotEmpty(indices)) {
- Arrays.sort(indices);
-
- int i = indices.length;
- int prevIndex = length;
- while (--i >= 0) {
- int index = indices[i];
- if (index < 0 || index >= length) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length);
- }
- if (index >= prevIndex) {
- continue;
- }
- diff++;
- prevIndex = index;
- }
- }
- Object result = Array.newInstance(array.getClass().getComponentType(), length - diff);
- if (diff < length) {
- int end = length;
- int dest = length - diff;
- for (int i = indices.length - 1; i >= 0; i--) {
- int index = indices[i];
- if (end - index > 1) {
- int cp = end - index - 1;
- dest -= cp;
- System.arraycopy(array, index + 1, result, dest, cp);
- }
- end = index;
- }
- if (end > 0) {
- System.arraycopy(array, 0, result, 0, end);
- }
- }
- return result;
- }
-
- /**
- * Extract a set of Integer indices into an int[].
- * @param coll {@code HashSet} of {@code Integer}
- * @return int[]
- * @since 3.0.1
- */
- private static int[] extractIndices(HashSet<Integer> coll) {
- int[] result = new int[coll.size()];
- int i = 0;
- for (Integer index : coll) {
- result[i++] = index.intValue();
- }
- return result;
- }
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.mutable.MutableInt; + +/** + * <p>Operations on arrays, primitive arrays (like {@code int[]}) and + * primitive wrapper arrays (like {@code Integer[]}).</p> + * + * <p>This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} + * array input. However, an Object array that contains a {@code null} + * element may throw an exception. Each method documents its behaviour.</p> + * + * <p>#ThreadSafe#</p> + * @since 2.0 + * @version $Id$ + */ +public class ArrayUtils { + + /** + * An empty immutable {@code Object} array. + */ + public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + /** + * An empty immutable {@code Class} array. + */ + public static final Class<?>[] EMPTY_CLASS_ARRAY = new Class[0]; + /** + * An empty immutable {@code String} array. + */ + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + /** + * An empty immutable {@code long} array. + */ + public static final long[] EMPTY_LONG_ARRAY = new long[0]; + /** + * An empty immutable {@code Long} array. + */ + public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0]; + /** + * An empty immutable {@code int} array. + */ + public static final int[] EMPTY_INT_ARRAY = new int[0]; + /** + * An empty immutable {@code Integer} array. + */ + public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0]; + /** + * An empty immutable {@code short} array. + */ + public static final short[] EMPTY_SHORT_ARRAY = new short[0]; + /** + * An empty immutable {@code Short} array. + */ + public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0]; + /** + * An empty immutable {@code byte} array. + */ + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + /** + * An empty immutable {@code Byte} array. + */ + public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0]; + /** + * An empty immutable {@code double} array. + */ + public static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; + /** + * An empty immutable {@code Double} array. + */ + public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0]; + /** + * An empty immutable {@code float} array. + */ + public static final float[] EMPTY_FLOAT_ARRAY = new float[0]; + /** + * An empty immutable {@code Float} array. + */ + public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0]; + /** + * An empty immutable {@code boolean} array. + */ + public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; + /** + * An empty immutable {@code Boolean} array. + */ + public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0]; + /** + * An empty immutable {@code char} array. + */ + public static final char[] EMPTY_CHAR_ARRAY = new char[0]; + /** + * An empty immutable {@code Character} array. + */ + public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; + + /** + * The index value when an element is not found in a list or array: {@code -1}. + * This value is returned by methods in this class and can also be used in comparisons with values returned by + * various method from {@link java.util.List}. + */ + public static final int INDEX_NOT_FOUND = -1; + + /** + * <p>ArrayUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as <code>ArrayUtils.clone(new int[] {2})</code>.</p> + * + * <p>This constructor is public to permit tools that require a JavaBean instance + * to operate.</p> + */ + public ArrayUtils() { + super(); + } + + + // NOTE: Cannot use {@code} to enclose text which includes {}, but <code></code> is OK + + + // Basic methods handling multi-dimensional arrays + //----------------------------------------------------------------------- + /** + * <p>Outputs an array as a String, treating {@code null} as an empty array.</p> + * + * <p>Multi-dimensional arrays are handled correctly, including + * multi-dimensional primitive arrays.</p> + * + * <p>The format is that of Java source code, for example <code>{a,b}</code>.</p> + * + * @param array the array to get a toString for, may be {@code null} + * @return a String representation of the array, '{}' if null array input + */ + public static String toString(Object array) { + return toString(array, "{}"); + } + + /** + * <p>Outputs an array as a String handling {@code null}s.</p> + * + * <p>Multi-dimensional arrays are handled correctly, including + * multi-dimensional primitive arrays.</p> + * + * <p>The format is that of Java source code, for example <code>{a,b}</code>.</p> + * + * @param array the array to get a toString for, may be {@code null} + * @param stringIfNull the String to return if the array is {@code null} + * @return a String representation of the array + */ + public static String toString(Object array, String stringIfNull) { + if (array == null) { + return stringIfNull; + } + return new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(array).toString(); + } + + /** + * <p>Get a hash code for an array handling multi-dimensional arrays correctly.</p> + * + * <p>Multi-dimensional primitive arrays are also handled correctly by this method.</p> + * + * @param array the array to get a hash code for, {@code null} returns zero + * @return a hash code for the array + */ + public static int hashCode(Object array) { + return new HashCodeBuilder().append(array).toHashCode(); + } + + /** + * <p>Compares two arrays, using equals(), handling multi-dimensional arrays + * correctly.</p> + * + * <p>Multi-dimensional primitive arrays are also handled correctly by this method.</p> + * + * @param array1 the left hand array to compare, may be {@code null} + * @param array2 the right hand array to compare, may be {@code null} + * @return {@code true} if the arrays are equal + */ + public static boolean isEquals(Object array1, Object array2) { + return new EqualsBuilder().append(array1, array2).isEquals(); + } + + // To map + //----------------------------------------------------------------------- + /** + * <p>Converts the given array into a {@link java.util.Map}. Each element of the array + * must be either a {@link java.util.Map.Entry} or an Array, containing at least two + * elements, where the first element is used as key and the second as + * value.</p> + * + * <p>This method can be used to initialize:</p> + * <pre> + * // Create a Map mapping colors. + * Map colorMap = MapUtils.toMap(new String[][] {{ + * {"RED", "#FF0000"}, + * {"GREEN", "#00FF00"}, + * {"BLUE", "#0000FF"}}); + * </pre> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array an array whose elements are either a {@link java.util.Map.Entry} or + * an Array containing at least two elements, may be {@code null} + * @return a {@code Map} that was created from the array + * @throws IllegalArgumentException if one element of this Array is + * itself an Array containing less then two elements + * @throws IllegalArgumentException if the array contains elements other + * than {@link java.util.Map.Entry} and an Array + */ + public static Map<Object, Object> toMap(Object[] array) { + if (array == null) { + return null; + } + final Map<Object, Object> map = new HashMap<Object, Object>((int) (array.length * 1.5)); + for (int i = 0; i < array.length; i++) { + Object object = array[i]; + if (object instanceof Map.Entry<?, ?>) { + Map.Entry<?,?> entry = (Map.Entry<?,?>) object; + map.put(entry.getKey(), entry.getValue()); + } else if (object instanceof Object[]) { + Object[] entry = (Object[]) object; + if (entry.length < 2) { + throw new IllegalArgumentException("Array element " + i + ", '" + + object + + "', has a length less than 2"); + } + map.put(entry[0], entry[1]); + } else { + throw new IllegalArgumentException("Array element " + i + ", '" + + object + + "', is neither of type Map.Entry nor an Array"); + } + } + return map; + } + + // Generic array + //----------------------------------------------------------------------- + /** + * <p>Create a type-safe generic array.</p> + * + * <p>The Java language does not allow an array to be created from a generic type:</p> + * + * <pre> + public static <T> T[] createAnArray(int size) { + return new T[size]; // compiler error here + } + public static <T> T[] createAnArray(int size) { + return (T[])new Object[size]; // ClassCastException at runtime + } + * </pre> + * + * <p>Therefore new arrays of generic types can be created with this method. + * For example, an array of Strings can be created:</p> + * + * <pre> + String[] array = ArrayUtils.toArray("1", "2"); + String[] emptyArray = ArrayUtils.<String>toArray(); + * </pre> + * + * <p>The method is typically used in scenarios, where the caller itself uses generic types + * that have to be combined into an array.</p> + * + * <p>Note, this method makes only sense to provide arguments of the same type so that the + * compiler can deduce the type of the array itself. While it is possible to select the + * type explicitly like in + * <code>Number[] array = ArrayUtils.<Number>toArray(Integer.valueOf(42), Double.valueOf(Math.PI))</code>, + * there is no real advantage when compared to + * <code>new Number[] {Integer.valueOf(42), Double.valueOf(Math.PI)}</code>.</p> + * + * @param <T> the array's element type + * @param items the varargs array items, null allowed + * @return the array, not null unless a null array is passed in + * @since 3.0 + */ + public static <T> T[] toArray(final T... items) { + return items; + } + + // Clone + //----------------------------------------------------------------------- + /** + * <p>Shallow clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>The objects in the array are not cloned, thus there is no special + * handling for multi-dimensional arrays.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param <T> the component type of the array + * @param array the array to shallow clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static <T> T[] clone(T[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static long[] clone(long[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static int[] clone(int[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static short[] clone(short[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static char[] clone(char[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static byte[] clone(byte[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static double[] clone(double[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static float[] clone(float[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + * <p>Clones an array returning a typecast result and handling + * {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static boolean[] clone(boolean[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + // nullToEmpty + //----------------------------------------------------------------------- + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Object[] nullToEmpty(Object[] array) { + if (array == null || array.length == 0) { + return EMPTY_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static String[] nullToEmpty(String[] array) { + if (array == null || array.length == 0) { + return EMPTY_STRING_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static long[] nullToEmpty(long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static int[] nullToEmpty(int[] array) { + if (array == null || array.length == 0) { + return EMPTY_INT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static short[] nullToEmpty(short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static char[] nullToEmpty(char[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static byte[] nullToEmpty(byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static double[] nullToEmpty(double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static float[] nullToEmpty(float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static boolean[] nullToEmpty(boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Long[] nullToEmpty(Long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Integer[] nullToEmpty(Integer[] array) { + if (array == null || array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Short[] nullToEmpty(Short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Character[] nullToEmpty(Character[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Byte[] nullToEmpty(Byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Double[] nullToEmpty(Double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Float[] nullToEmpty(Float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; + } + return array; + } + + /** + * <p>Defensive programming technique to change a {@code null} + * reference to an empty one.</p> + * + * <p>This method returns an empty array for a {@code null} input array.</p> + * + * <p>As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.</p> + * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Boolean[] nullToEmpty(Boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; + } + return array; + } + + // Subarrays + //----------------------------------------------------------------------- + /** + * <p>Produces a new array containing the elements between + * the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * <p>The component type of the subarray is always the same as + * that of the input array. Thus, if the input is an array of type + * {@code Date}, the following usage is envisaged:</p> + * + * <pre> + * Date[] someDates = (Date[])ArrayUtils.subarray(allDates, 2, 5); + * </pre> + * + * @param <T> the component type of the array + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static <T> T[] subarray(T[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + Class<?> type = array.getClass().getComponentType(); + if (newSize <= 0) { + @SuppressWarnings("unchecked") // OK, because array is of type T + final T[] emptyArray = (T[]) Array.newInstance(type, 0); + return emptyArray; + } + @SuppressWarnings("unchecked") // OK, because array is of type T + T[] subarray = (T[]) Array.newInstance(type, newSize); + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code long} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static long[] subarray(long[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_LONG_ARRAY; + } + + long[] subarray = new long[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code int} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static int[] subarray(int[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_INT_ARRAY; + } + + int[] subarray = new int[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code short} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static short[] subarray(short[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_SHORT_ARRAY; + } + + short[] subarray = new short[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code char} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static char[] subarray(char[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_CHAR_ARRAY; + } + + char[] subarray = new char[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code byte} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BYTE_ARRAY; + } + + byte[] subarray = new byte[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code double} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static double[] subarray(double[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_DOUBLE_ARRAY; + } + + double[] subarray = new double[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code float} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static float[] subarray(float[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_FLOAT_ARRAY; + } + + float[] subarray = new float[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + * <p>Produces a new {@code boolean} array containing the elements + * between the start and end indices.</p> + * + * <p>The start index is inclusive, the end index exclusive. + * Null array input produces null output.</p> + * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static boolean[] subarray(boolean[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BOOLEAN_ARRAY; + } + + boolean[] subarray = new boolean[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + // Is same length + //----------------------------------------------------------------------- + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}. + * + * <p>Any multi-dimensional aspects of the arrays are ignored.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(Object[] array1, Object[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(long[] array1, long[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(int[] array1, int[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(short[] array1, short[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(char[] array1, char[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(byte[] array1, byte[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(double[] array1, double[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(float[] array1, float[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + * <p>Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.</p> + * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(boolean[] array1, boolean[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + //----------------------------------------------------------------------- + /** + * <p>Returns the length of the specified array. + * This method can deal with {@code Object} arrays and with primitive arrays.</p> + * + * <p>If the input array is {@code null}, {@code 0} is returned.</p> + * + * <pre> + * ArrayUtils.getLength(null) = 0 + * ArrayUtils.getLength([]) = 0 + * ArrayUtils.getLength([null]) = 1 + * ArrayUtils.getLength([true, false]) = 2 + * ArrayUtils.getLength([1, 2, 3]) = 3 + * ArrayUtils.getLength(["a", "b", "c"]) = 3 + * </pre> + * + * @param array the array to retrieve the length from, may be null + * @return The length of the array, or {@code 0} if the array is {@code null} + * @throws IllegalArgumentException if the object arguement is not an array. + * @since 2.1 + */ + public static int getLength(Object array) { + if (array == null) { + return 0; + } + return Array.getLength(array); + } + + /** + * <p>Checks whether two arrays are the same type taking into account + * multi-dimensional arrays.</p> + * + * @param array1 the first array, must not be {@code null} + * @param array2 the second array, must not be {@code null} + * @return {@code true} if type of arrays matches + * @throws IllegalArgumentException if either array is {@code null} + */ + public static boolean isSameType(Object array1, Object array2) { + if (array1 == null || array2 == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + return array1.getClass().getName().equals(array2.getClass().getName()); + } + + // Reverse + //----------------------------------------------------------------------- + /** + * <p>Reverses the order of the given array.</p> + * + * <p>There is no special handling for multi-dimensional arrays.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(Object[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + Object tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(long[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + long tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(int[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + int tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(short[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + short tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(char[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + char tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(byte[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(double[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + double tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(float[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + float tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + * <p>Reverses the order of the given array.</p> + * + * <p>This method does nothing for a {@code null} input array.</p> + * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(boolean[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + boolean tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // IndexOf search + // ---------------------------------------------------------------------- + + // Object IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given object in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @return the index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(Object[] array, Object objectToFind) { + return indexOf(array, objectToFind, 0); + } + + /** + * <p>Finds the index of the given object in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @param startIndex the index to start searching at + * @return the index of the object within the array starting at the index, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(Object[] array, Object objectToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + if (objectToFind == null) { + for (int i = startIndex; i < array.length; i++) { + if (array[i] == null) { + return i; + } + } + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { + for (int i = startIndex; i < array.length; i++) { + if (objectToFind.equals(array[i])) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given object within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @return the last index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(Object[] array, Object objectToFind) { + return lastIndexOf(array, objectToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given object in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than + * the array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @param startIndex the start index to travers backwards from + * @return the last index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(Object[] array, Object objectToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + if (objectToFind == null) { + for (int i = startIndex; i >= 0; i--) { + if (array[i] == null) { + return i; + } + } + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { + for (int i = startIndex; i >= 0; i--) { + if (objectToFind.equals(array[i])) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the object is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param objectToFind the object to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(Object[] array, Object objectToFind) { + return indexOf(array, objectToFind) != INDEX_NOT_FOUND; + } + + // long IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(long[] array, long valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(long[] array, long valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(long[] array, long valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(long[] array, long valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(long[] array, long valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // int IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(int[] array, int valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(int[] array, int valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(int[] array, int valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(int[] array, int valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(int[] array, int valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // short IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(short[] array, short valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(short[] array, short valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(short[] array, short valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(short[] array, short valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(short[] array, short valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // char IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + * @since 2.1 + */ + public static boolean contains(char[] array, char valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // byte IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(byte[] array, byte valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(byte[] array, byte valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(byte[] array, byte valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(byte[] array, byte valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(byte[] array, byte valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // double IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value within a given tolerance in the array. + * This method will return the index of the first value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, double tolerance) { + return indexOf(array, valueToFind, 0, tolerance); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the index of the given value in the array starting at the given index. + * This method will return the index of the first value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + double min = valueToFind - tolerance; + double max = valueToFind + tolerance; + for (int i = startIndex; i < array.length; i++) { + if (array[i] >= min && array[i] <= max) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value within a given tolerance in the array. + * This method will return the index of the last value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, double tolerance) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index. + * This method will return the index of the last value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @param tolerance search for value within plus/minus this amount + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + double min = valueToFind - tolerance; + double max = valueToFind + tolerance; + for (int i = startIndex; i >= 0; i--) { + if (array[i] >= min && array[i] <= max) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(double[] array, double valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + /** + * <p>Checks if a value falling within the given tolerance is in the + * given array. If the array contains a value within the inclusive range + * defined by (value - tolerance) to (value + tolerance).</p> + * + * <p>The method returns {@code false} if a {@code null} array + * is passed in.</p> + * + * @param array the array to search + * @param valueToFind the value to find + * @param tolerance the array contains the tolerance of the search + * @return true if value falling within tolerance is in array + */ + public static boolean contains(double[] array, double valueToFind, double tolerance) { + return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND; + } + + // float IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(float[] array, float valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(float[] array, float valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(float[] array, float valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // boolean IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the index of the given value in the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(boolean[] array, boolean valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + * <p>Finds the index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p> + * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} + * array input + */ + public static int indexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Finds the last index of the given value within the array.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) if + * {@code null} array input.</p> + * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(boolean[] array, boolean valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + * <p>Finds the last index of the given value in the array starting at the given index.</p> + * + * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p> + * + * <p>A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than + * the array length will search from the end of the array.</p> + * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the value is in the given array.</p> + * + * <p>The method returns {@code false} if a {@code null} array is passed in.</p> + * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(boolean[] array, boolean valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // Primitive/Object array converters + // ---------------------------------------------------------------------- + + // Character array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Characters to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Character} array, may be {@code null} + * @return a {@code char} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static char[] toPrimitive(Character[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].charValue(); + } + return result; + } + + /** + * <p>Converts an array of object Character to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Character} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code char} array, {@code null} if null array input + */ + public static char[] toPrimitive(Character[] array, char valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + Character b = array[i]; + result[i] = (b == null ? valueForNull : b.charValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive chars to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code char} array + * @return a {@code Character} array, {@code null} if null array input + */ + public static Character[] toObject(char[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + final Character[] result = new Character[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Character.valueOf(array[i]); + } + return result; + } + + // Long array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Longs to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Long} array, may be {@code null} + * @return a {@code long} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static long[] toPrimitive(Long[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_ARRAY; + } + final long[] result = new long[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].longValue(); + } + return result; + } + + /** + * <p>Converts an array of object Long to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Long} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code long} array, {@code null} if null array input + */ + public static long[] toPrimitive(Long[] array, long valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_ARRAY; + } + final long[] result = new long[array.length]; + for (int i = 0; i < array.length; i++) { + Long b = array[i]; + result[i] = (b == null ? valueForNull : b.longValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive longs to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code long} array + * @return a {@code Long} array, {@code null} if null array input + */ + public static Long[] toObject(long[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; + } + final Long[] result = new Long[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Long.valueOf(array[i]); + } + return result; + } + + // Int array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Integers to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Integer} array, may be {@code null} + * @return an {@code int} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static int[] toPrimitive(Integer[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INT_ARRAY; + } + final int[] result = new int[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].intValue(); + } + return result; + } + + /** + * <p>Converts an array of object Integer to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Integer} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return an {@code int} array, {@code null} if null array input + */ + public static int[] toPrimitive(Integer[] array, int valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INT_ARRAY; + } + final int[] result = new int[array.length]; + for (int i = 0; i < array.length; i++) { + Integer b = array[i]; + result[i] = (b == null ? valueForNull : b.intValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive ints to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array an {@code int} array + * @return an {@code Integer} array, {@code null} if null array input + */ + public static Integer[] toObject(int[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; + } + final Integer[] result = new Integer[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Integer.valueOf(array[i]); + } + return result; + } + + // Short array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Shorts to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Short} array, may be {@code null} + * @return a {@code byte} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static short[] toPrimitive(Short[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + final short[] result = new short[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].shortValue(); + } + return result; + } + + /** + * <p>Converts an array of object Short to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Short} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code byte} array, {@code null} if null array input + */ + public static short[] toPrimitive(Short[] array, short valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + final short[] result = new short[array.length]; + for (int i = 0; i < array.length; i++) { + Short b = array[i]; + result[i] = (b == null ? valueForNull : b.shortValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive shorts to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code short} array + * @return a {@code Short} array, {@code null} if null array input + */ + public static Short[] toObject(short[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; + } + final Short[] result = new Short[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Short.valueOf(array[i]); + } + return result; + } + + // Byte array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Bytes to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Byte} array, may be {@code null} + * @return a {@code byte} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static byte[] toPrimitive(Byte[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + final byte[] result = new byte[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].byteValue(); + } + return result; + } + + /** + * <p>Converts an array of object Bytes to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Byte} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code byte} array, {@code null} if null array input + */ + public static byte[] toPrimitive(Byte[] array, byte valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + final byte[] result = new byte[array.length]; + for (int i = 0; i < array.length; i++) { + Byte b = array[i]; + result[i] = (b == null ? valueForNull : b.byteValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive bytes to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code byte} array + * @return a {@code Byte} array, {@code null} if null array input + */ + public static Byte[] toObject(byte[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; + } + final Byte[] result = new Byte[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Byte.valueOf(array[i]); + } + return result; + } + + // Double array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Doubles to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Double} array, may be {@code null} + * @return a {@code double} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static double[] toPrimitive(Double[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + final double[] result = new double[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].doubleValue(); + } + return result; + } + + /** + * <p>Converts an array of object Doubles to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Double} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code double} array, {@code null} if null array input + */ + public static double[] toPrimitive(Double[] array, double valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + final double[] result = new double[array.length]; + for (int i = 0; i < array.length; i++) { + Double b = array[i]; + result[i] = (b == null ? valueForNull : b.doubleValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive doubles to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code double} array + * @return a {@code Double} array, {@code null} if null array input + */ + public static Double[] toObject(double[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; + } + final Double[] result = new Double[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Double.valueOf(array[i]); + } + return result; + } + + // Float array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Floats to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Float} array, may be {@code null} + * @return a {@code float} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static float[] toPrimitive(Float[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + final float[] result = new float[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].floatValue(); + } + return result; + } + + /** + * <p>Converts an array of object Floats to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Float} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code float} array, {@code null} if null array input + */ + public static float[] toPrimitive(Float[] array, float valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + final float[] result = new float[array.length]; + for (int i = 0; i < array.length; i++) { + Float b = array[i]; + result[i] = (b == null ? valueForNull : b.floatValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive floats to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code float} array + * @return a {@code Float} array, {@code null} if null array input + */ + public static Float[] toObject(float[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; + } + final Float[] result = new Float[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Float.valueOf(array[i]); + } + return result; + } + + // Boolean array converters + // ---------------------------------------------------------------------- + /** + * <p>Converts an array of object Booleans to primitives.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Boolean} array, may be {@code null} + * @return a {@code boolean} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static boolean[] toPrimitive(Boolean[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + final boolean[] result = new boolean[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].booleanValue(); + } + return result; + } + + /** + * <p>Converts an array of object Booleans to primitives handling {@code null}.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code Boolean} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code boolean} array, {@code null} if null array input + */ + public static boolean[] toPrimitive(Boolean[] array, boolean valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + final boolean[] result = new boolean[array.length]; + for (int i = 0; i < array.length; i++) { + Boolean b = array[i]; + result[i] = (b == null ? valueForNull : b.booleanValue()); + } + return result; + } + + /** + * <p>Converts an array of primitive booleans to objects.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array a {@code boolean} array + * @return a {@code Boolean} array, {@code null} if null array input + */ + public static Boolean[] toObject(boolean[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; + } + final Boolean[] result = new Boolean[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE); + } + return result; + } + + // ---------------------------------------------------------------------- + /** + * <p>Checks if an array of Objects is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(Object[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive longs is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(long[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive ints is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(int[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive shorts is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(short[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive chars is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(char[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive bytes is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(byte[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive doubles is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(double[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive floats is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(float[] array) { + return array == null || array.length == 0; + } + + /** + * <p>Checks if an array of primitive booleans is empty or {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(boolean[] array) { + return array == null || array.length == 0; + } + + // ---------------------------------------------------------------------- + /** + * <p>Checks if an array of Objects is not empty or not {@code null}.</p> + * + * @param <T> the component type of the array + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static <T> boolean isNotEmpty(T[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive longs is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(long[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive ints is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(int[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive shorts is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(short[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive chars is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(char[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive bytes is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(byte[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive doubles is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(double[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive floats is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(float[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Checks if an array of primitive booleans is not empty or not {@code null}.</p> + * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(boolean[] array) { + return (array != null && array.length != 0); + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(null, null) = null + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * ArrayUtils.addAll([null], [null]) = [null, null] + * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"] + * </pre> + * + * @param <T> the component type of the array + * @param array1 the first array whose elements are added to the new array, may be {@code null} + * @param array2 the second array whose elements are added to the new array, may be {@code null} + * @return The new array, {@code null} if both arrays are {@code null}. + * The type of the new array is the type of the first array, + * unless the first array is null, in which case the type is the same as the second array. + * @since 2.1 + * @throws IllegalArgumentException if the array types are incompatible + */ + public static <T> T[] addAll(T[] array1, T... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + final Class<?> type1 = array1.getClass().getComponentType(); + @SuppressWarnings("unchecked") // OK, because array is of type T + T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length); + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + try { + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + } catch (ArrayStoreException ase) { + // Check if problem was due to incompatible types + /* + * We do this here, rather than before the copy because: + * - it would be a wasted check most of the time + * - safer, in case check turns out to be too strict + */ + final Class<?> type2 = array2.getClass().getComponentType(); + if (!type1.isAssignableFrom(type2)){ + throw new IllegalArgumentException("Cannot store "+type2.getName()+" in an array of " + +type1.getName(), ase); + } + throw ase; // No, so rethrow original + } + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new boolean[] array. + * @since 2.1 + */ + public static boolean[] addAll(boolean[] array1, boolean... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + boolean[] joinedArray = new boolean[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new char[] array. + * @since 2.1 + */ + public static char[] addAll(char[] array1, char... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + char[] joinedArray = new char[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new byte[] array. + * @since 2.1 + */ + public static byte[] addAll(byte[] array1, byte... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + byte[] joinedArray = new byte[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new short[] array. + * @since 2.1 + */ + public static short[] addAll(short[] array1, short... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + short[] joinedArray = new short[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new int[] array. + * @since 2.1 + */ + public static int[] addAll(int[] array1, int... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + int[] joinedArray = new int[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new long[] array. + * @since 2.1 + */ + public static long[] addAll(long[] array1, long... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + long[] joinedArray = new long[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new float[] array. + * @since 2.1 + */ + public static float[] addAll(float[] array1, float... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + float[] joinedArray = new float[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Adds all the elements of the given arrays into a new array.</p> + * <p>The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.</p> + * + * <pre> + * ArrayUtils.addAll(array1, null) = cloned copy of array1 + * ArrayUtils.addAll(null, array2) = cloned copy of array2 + * ArrayUtils.addAll([], []) = [] + * </pre> + * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new double[] array. + * @since 2.1 + */ + public static double[] addAll(double[] array1, double... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + double[] joinedArray = new double[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element, unless the element itself is null, + * in which case the return type is Object[]</p> + * + * <pre> + * ArrayUtils.add(null, null) = [null] + * ArrayUtils.add(null, "a") = ["a"] + * ArrayUtils.add(["a"], null) = ["a", null] + * ArrayUtils.add(["a"], "b") = ["a", "b"] + * ArrayUtils.add(["a", "b"], "c") = ["a", "b", "c"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to "add" the element to, may be {@code null} + * @param element the object to add, may be {@code null} + * @return A new array containing the existing elements plus the new element + * The returned array type will be that of the input array (unless null), + * in which case it will have the same type as the element. + * If both are null, an IllegalArgumentException is thrown + * @since 2.1 + * @throws IllegalArgumentException if both arguments are null + */ + public static <T> T[] add(T[] array, T element) { + Class<?> type; + if (array != null){ + type = array.getClass(); + } else if (element != null) { + type = element.getClass(); + } else { + throw new IllegalArgumentException("Arguments cannot both be null"); + } + @SuppressWarnings("unchecked") // type must be T + T[] newArray = (T[]) copyArrayGrow1(array, type); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, true) = [true] + * ArrayUtils.add([true], false) = [true, false] + * ArrayUtils.add([true, false], true) = [true, false, true] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static boolean[] add(boolean[] array, boolean element) { + boolean[] newArray = (boolean[])copyArrayGrow1(array, Boolean.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static byte[] add(byte[] array, byte element) { + byte[] newArray = (byte[])copyArrayGrow1(array, Byte.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, '0') = ['0'] + * ArrayUtils.add(['1'], '0') = ['1', '0'] + * ArrayUtils.add(['1', '0'], '1') = ['1', '0', '1'] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static char[] add(char[] array, char element) { + char[] newArray = (char[])copyArrayGrow1(array, Character.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static double[] add(double[] array, double element) { + double[] newArray = (double[])copyArrayGrow1(array, Double.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static float[] add(float[] array, float element) { + float[] newArray = (float[])copyArrayGrow1(array, Float.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static int[] add(int[] array, int element) { + int[] newArray = (int[])copyArrayGrow1(array, Integer.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static long[] add(long[] array, long element) { + long[] newArray = (long[])copyArrayGrow1(array, Long.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * <p>Copies the given array and adds the given element at the end of the new array.</p> + * + * <p>The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0) = [0] + * ArrayUtils.add([1], 0) = [1, 0] + * ArrayUtils.add([1, 0], 1) = [1, 0, 1] + * </pre> + * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static short[] add(short[] array, short element) { + short[] newArray = (short[])copyArrayGrow1(array, Short.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * Returns a copy of the given array of size 1 greater than the argument. + * The last value of the array is left to the default value. + * + * @param array The array to copy, must not be {@code null}. + * @param newArrayComponentType If {@code array} is {@code null}, create a + * size 1 array of this type. + * @return A new copy of the array of size 1 greater than the input. + */ + private static Object copyArrayGrow1(Object array, Class<?> newArrayComponentType) { + if (array != null) { + int arrayLength = Array.getLength(array); + Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1); + System.arraycopy(array, 0, newArray, 0, arrayLength); + return newArray; + } + return Array.newInstance(newArrayComponentType, 1); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0, null) = [null] + * ArrayUtils.add(null, 0, "a") = ["a"] + * ArrayUtils.add(["a"], 1, null) = ["a", null] + * ArrayUtils.add(["a"], 1, "b") = ["a", "b"] + * ArrayUtils.add(["a", "b"], 3, "c") = ["a", "b", "c"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + * @throws IllegalArgumentException if both array and element are null + */ + public static <T> T[] add(T[] array, int index, T element) { + Class<?> clss = null; + if (array != null) { + clss = array.getClass().getComponentType(); + } else if (element != null) { + clss = element.getClass(); + } else { + throw new IllegalArgumentException("Array and element cannot both be null"); + } + @SuppressWarnings("unchecked") // the add method creates an array of type clss, which is type T + final T[] newArray = (T[]) add(array, index, element, clss); + return newArray; + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0, true) = [true] + * ArrayUtils.add([true], 0, false) = [false, true] + * ArrayUtils.add([false], 1, true) = [false, true] + * ArrayUtils.add([true, false], 1, true) = [true, true, false] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static boolean[] add(boolean[] array, int index, boolean element) { + return (boolean[]) add(array, index, Boolean.valueOf(element), Boolean.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add(null, 0, 'a') = ['a'] + * ArrayUtils.add(['a'], 0, 'b') = ['b', 'a'] + * ArrayUtils.add(['a', 'b'], 0, 'c') = ['c', 'a', 'b'] + * ArrayUtils.add(['a', 'b'], 1, 'k') = ['a', 'k', 'b'] + * ArrayUtils.add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c'] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static char[] add(char[] array, int index, char element) { + return (char[]) add(array, index, Character.valueOf(element), Character.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1], 0, 2) = [2, 1] + * ArrayUtils.add([2, 6], 2, 3) = [2, 6, 3] + * ArrayUtils.add([2, 6], 0, 1) = [1, 2, 6] + * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static byte[] add(byte[] array, int index, byte element) { + return (byte[]) add(array, index, Byte.valueOf(element), Byte.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1], 0, 2) = [2, 1] + * ArrayUtils.add([2, 6], 2, 10) = [2, 6, 10] + * ArrayUtils.add([2, 6], 0, -4) = [-4, 2, 6] + * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static short[] add(short[] array, int index, short element) { + return (short[]) add(array, index, Short.valueOf(element), Short.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1], 0, 2) = [2, 1] + * ArrayUtils.add([2, 6], 2, 10) = [2, 6, 10] + * ArrayUtils.add([2, 6], 0, -4) = [-4, 2, 6] + * ArrayUtils.add([2, 6, 3], 2, 1) = [2, 6, 1, 3] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static int[] add(int[] array, int index, int element) { + return (int[]) add(array, index, Integer.valueOf(element), Integer.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1L], 0, 2L) = [2L, 1L] + * ArrayUtils.add([2L, 6L], 2, 10L) = [2L, 6L, 10L] + * ArrayUtils.add([2L, 6L], 0, -4L) = [-4L, 2L, 6L] + * ArrayUtils.add([2L, 6L, 3L], 2, 1L) = [2L, 6L, 1L, 3L] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static long[] add(long[] array, int index, long element) { + return (long[]) add(array, index, Long.valueOf(element), Long.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1.1f], 0, 2.2f) = [2.2f, 1.1f] + * ArrayUtils.add([2.3f, 6.4f], 2, 10.5f) = [2.3f, 6.4f, 10.5f] + * ArrayUtils.add([2.6f, 6.7f], 0, -4.8f) = [-4.8f, 2.6f, 6.7f] + * ArrayUtils.add([2.9f, 6.0f, 0.3f], 2, 1.0f) = [2.9f, 6.0f, 1.0f, 0.3f] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static float[] add(float[] array, int index, float element) { + return (float[]) add(array, index, Float.valueOf(element), Float.TYPE); + } + + /** + * <p>Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.</p> + * + * <pre> + * ArrayUtils.add([1.1], 0, 2.2) = [2.2, 1.1] + * ArrayUtils.add([2.3, 6.4], 2, 10.5) = [2.3, 6.4, 10.5] + * ArrayUtils.add([2.6, 6.7], 0, -4.8) = [-4.8, 2.6, 6.7] + * ArrayUtils.add([2.9, 6.0, 0.3], 2, 1.0) = [2.9, 6.0, 1.0, 0.3] + * </pre> + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static double[] add(double[] array, int index, double element) { + return (double[]) add(array, index, Double.valueOf(element), Double.TYPE); + } + + /** + * Underlying implementation of add(array, index, element) methods. + * The last parameter is the class, which may not equal element.getClass + * for primitives. + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @param clss the type of the element being added + * @return A new array containing the existing elements and the new element + */ + private static Object add(Object array, int index, Object element, Class<?> clss) { + if (array == null) { + if (index != 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: 0"); + } + Object joinedArray = Array.newInstance(clss, 1); + Array.set(joinedArray, 0, element); + return joinedArray; + } + int length = Array.getLength(array); + if (index > length || index < 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + Object result = Array.newInstance(clss, length + 1); + System.arraycopy(array, 0, result, 0, index); + Array.set(result, index, element); + if (index < length) { + System.arraycopy(array, index, result, index + 1, length - index); + } + return result; + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove(["a"], 0) = [] + * ArrayUtils.remove(["a", "b"], 0) = ["b"] + * ArrayUtils.remove(["a", "b"], 1) = ["a"] + * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + @SuppressWarnings("unchecked") // remove() always creates an array of the same type as its input + public static <T> T[] remove(T[] array, int index) { + return (T[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, "a") = null + * ArrayUtils.removeElement([], "a") = [] + * ArrayUtils.removeElement(["a"], "b") = ["a"] + * ArrayUtils.removeElement(["a", "b"], "a") = ["b"] + * ArrayUtils.removeElement(["a", "b", "a"], "a") = ["b", "a"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static <T> T[] removeElement(T[] array, Object element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([true], 0) = [] + * ArrayUtils.remove([true, false], 0) = [false] + * ArrayUtils.remove([true, false], 1) = [true] + * ArrayUtils.remove([true, true, false], 1) = [true, false] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static boolean[] remove(boolean[] array, int index) { + return (boolean[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, true) = null + * ArrayUtils.removeElement([], true) = [] + * ArrayUtils.removeElement([true], false) = [true] + * ArrayUtils.removeElement([true, false], false) = [true] + * ArrayUtils.removeElement([true, false, true], true) = [false, true] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static boolean[] removeElement(boolean[] array, boolean element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1], 0) = [] + * ArrayUtils.remove([1, 0], 0) = [0] + * ArrayUtils.remove([1, 0], 1) = [1] + * ArrayUtils.remove([1, 0, 1], 1) = [1, 1] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static byte[] remove(byte[] array, int index) { + return (byte[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1) = null + * ArrayUtils.removeElement([], 1) = [] + * ArrayUtils.removeElement([1], 0) = [1] + * ArrayUtils.removeElement([1, 0], 0) = [1] + * ArrayUtils.removeElement([1, 0, 1], 1) = [0, 1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static byte[] removeElement(byte[] array, byte element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove(['a'], 0) = [] + * ArrayUtils.remove(['a', 'b'], 0) = ['b'] + * ArrayUtils.remove(['a', 'b'], 1) = ['a'] + * ArrayUtils.remove(['a', 'b', 'c'], 1) = ['a', 'c'] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static char[] remove(char[] array, int index) { + return (char[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 'a') = null + * ArrayUtils.removeElement([], 'a') = [] + * ArrayUtils.removeElement(['a'], 'b') = ['a'] + * ArrayUtils.removeElement(['a', 'b'], 'a') = ['b'] + * ArrayUtils.removeElement(['a', 'b', 'a'], 'a') = ['b', 'a'] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static char[] removeElement(char[] array, char element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1.1], 0) = [] + * ArrayUtils.remove([2.5, 6.0], 0) = [6.0] + * ArrayUtils.remove([2.5, 6.0], 1) = [2.5] + * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static double[] remove(double[] array, int index) { + return (double[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1.1) = null + * ArrayUtils.removeElement([], 1.1) = [] + * ArrayUtils.removeElement([1.1], 1.2) = [1.1] + * ArrayUtils.removeElement([1.1, 2.3], 1.1) = [2.3] + * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static double[] removeElement(double[] array, double element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1.1], 0) = [] + * ArrayUtils.remove([2.5, 6.0], 0) = [6.0] + * ArrayUtils.remove([2.5, 6.0], 1) = [2.5] + * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static float[] remove(float[] array, int index) { + return (float[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1.1) = null + * ArrayUtils.removeElement([], 1.1) = [] + * ArrayUtils.removeElement([1.1], 1.2) = [1.1] + * ArrayUtils.removeElement([1.1, 2.3], 1.1) = [2.3] + * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static float[] removeElement(float[] array, float element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1], 0) = [] + * ArrayUtils.remove([2, 6], 0) = [6] + * ArrayUtils.remove([2, 6], 1) = [2] + * ArrayUtils.remove([2, 6, 3], 1) = [2, 3] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static int[] remove(int[] array, int index) { + return (int[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1) = null + * ArrayUtils.removeElement([], 1) = [] + * ArrayUtils.removeElement([1], 2) = [1] + * ArrayUtils.removeElement([1, 3], 1) = [3] + * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static int[] removeElement(int[] array, int element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1], 0) = [] + * ArrayUtils.remove([2, 6], 0) = [6] + * ArrayUtils.remove([2, 6], 1) = [2] + * ArrayUtils.remove([2, 6, 3], 1) = [2, 3] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static long[] remove(long[] array, int index) { + return (long[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1) = null + * ArrayUtils.removeElement([], 1) = [] + * ArrayUtils.removeElement([1], 2) = [1] + * ArrayUtils.removeElement([1, 3], 1) = [3] + * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static long[] removeElement(long[] array, long element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.remove([1], 0) = [] + * ArrayUtils.remove([2, 6], 0) = [6] + * ArrayUtils.remove([2, 6], 1) = [2] + * ArrayUtils.remove([2, 6, 3], 1) = [2, 3] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static short[] remove(short[] array, int index) { + return (short[]) remove((Object) array, index); + } + + /** + * <p>Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <pre> + * ArrayUtils.removeElement(null, 1) = null + * ArrayUtils.removeElement([], 1) = [] + * ArrayUtils.removeElement([1], 2) = [1] + * ArrayUtils.removeElement([1, 3], 1) = [3] + * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static short[] removeElement(short[] array, short element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + * <p>Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).</p> + * + * <p>This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + private static Object remove(Object array, int index) { + int length = getLength(array); + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + + Object result = Array.newInstance(array.getClass().getComponentType(), length - 1); + System.arraycopy(array, 0, result, 0, index); + if (index < length - 1) { + System.arraycopy(array, index + 1, result, index, length - index - 1); + } + + return result; + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll(["a", "b", "c"], 0, 2) = ["b"] + * ArrayUtils.removeAll(["a", "b", "c"], 1, 2) = ["a"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + @SuppressWarnings("unchecked") + // removeAll() always creates an array of the same type as its input + public static <T> T[] removeAll(T[] array, int... indices) { + return (T[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, "a", "b") = null + * ArrayUtils.removeElements([], "a", "b") = [] + * ArrayUtils.removeElements(["a"], "b", "c") = ["a"] + * ArrayUtils.removeElements(["a", "b"], "a", "c") = ["b"] + * ArrayUtils.removeElements(["a", "b", "a"], "a") = ["b", "a"] + * ArrayUtils.removeElements(["a", "b", "a"], "a", "a") = ["b"] + * </pre> + * + * @param <T> the component type of the array + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static <T> T[] removeElements(T[] array, T... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(values.length); + for (T v : values) { + MutableInt count = occurrences.get(v); + if (count == null) { + occurrences.put(v, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) { + T v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v, found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static byte[] removeAll(byte[] array, int... indices) { + return (byte[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static byte[] removeElements(byte[] array, byte... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Byte, MutableInt> occurrences = new HashMap<Byte, MutableInt>(values.length); + for (byte v : values) { + Byte boxed = Byte.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Byte, MutableInt> e : occurrences.entrySet()) { + Byte v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.byteValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static short[] removeAll(short[] array, int... indices) { + return (short[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static short[] removeElements(short[] array, short... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Short, MutableInt> occurrences = new HashMap<Short, MutableInt>(values.length); + for (short v : values) { + Short boxed = Short.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Short, MutableInt> e : occurrences.entrySet()) { + Short v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.shortValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static int[] removeAll(int[] array, int... indices) { + return (int[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static int[] removeElements(int[] array, int... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Integer, MutableInt> occurrences = new HashMap<Integer, MutableInt>(values.length); + for (int v : values) { + Integer boxed = Integer.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Integer, MutableInt> e : occurrences.entrySet()) { + Integer v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.intValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static char[] removeAll(char[] array, int... indices) { + return (char[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static char[] removeElements(char[] array, char... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Character, MutableInt> occurrences = new HashMap<Character, MutableInt>(values.length); + for (char v : values) { + Character boxed = Character.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Character, MutableInt> e : occurrences.entrySet()) { + Character v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.charValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static long[] removeAll(long[] array, int... indices) { + return (long[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static long[] removeElements(long[] array, long... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Long, MutableInt> occurrences = new HashMap<Long, MutableInt>(values.length); + for (long v : values) { + Long boxed = Long.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Long, MutableInt> e : occurrences.entrySet()) { + Long v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.longValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static float[] removeAll(float[] array, int... indices) { + return (float[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static float[] removeElements(float[] array, float... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Float, MutableInt> occurrences = new HashMap<Float, MutableInt>(values.length); + for (float v : values) { + Float boxed = Float.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Float, MutableInt> e : occurrences.entrySet()) { + Float v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.floatValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([1], 0) = [] + * ArrayUtils.removeAll([2, 6], 0) = [6] + * ArrayUtils.removeAll([2, 6], 0, 1) = [] + * ArrayUtils.removeAll([2, 6, 3], 1, 2) = [2] + * ArrayUtils.removeAll([2, 6, 3], 0, 2) = [6] + * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = [] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static double[] removeAll(double[] array, int... indices) { + return (double[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, 1, 2) = null + * ArrayUtils.removeElements([], 1, 2) = [] + * ArrayUtils.removeElements([1], 2, 3) = [1] + * ArrayUtils.removeElements([1, 3], 1, 2) = [3] + * ArrayUtils.removeElements([1, 3, 1], 1) = [3, 1] + * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static double[] removeElements(double[] array, double... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Double, MutableInt> occurrences = new HashMap<Double, MutableInt>(values.length); + for (double v : values) { + Double boxed = Double.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Double, MutableInt> e : occurrences.entrySet()) { + Double v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.doubleValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * <p>Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.</p> + * + * <p>If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.</p> + * + * <pre> + * ArrayUtils.removeAll([true, false, true], 0, 2) = [false] + * ArrayUtils.removeAll([true, false, true], 1, 2) = [true] + * </pre> + * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static boolean[] removeAll(boolean[] array, int... indices) { + return (boolean[]) removeAll((Object) array, clone(indices)); + } + + /** + * <p>Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.</p> + * + * <p>This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.</p> + * + * <pre> + * ArrayUtils.removeElements(null, true, false) = null + * ArrayUtils.removeElements([], true, false) = [] + * ArrayUtils.removeElements([true], false, false) = [true] + * ArrayUtils.removeElements([true, false], true, true) = [false] + * ArrayUtils.removeElements([true, false, true], true) = [false, true] + * ArrayUtils.removeElements([true, false, true], true, true) = [false] + * </pre> + * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static boolean[] removeElements(boolean[] array, boolean... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap<Boolean, MutableInt> occurrences = new HashMap<Boolean, MutableInt>(values.length); + for (boolean v : values) { + Boolean boxed = Boolean.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet<Integer> toRemove = new HashSet<Integer>(); + for (Map.Entry<Boolean, MutableInt> e : occurrences.entrySet()) { + Boolean v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.booleanValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * Removes multiple array elements specified by index. + * @param array source + * @param indices to remove, WILL BE SORTED--so only clones of user-owned arrays! + * @return new array of same type minus elements specified by unique values of {@code indices} + * @since 3.0.1 + */ + private static Object removeAll(Object array, int... indices) { + int length = getLength(array); + int diff = 0; + + if (isNotEmpty(indices)) { + Arrays.sort(indices); + + int i = indices.length; + int prevIndex = length; + while (--i >= 0) { + int index = indices[i]; + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + if (index >= prevIndex) { + continue; + } + diff++; + prevIndex = index; + } + } + Object result = Array.newInstance(array.getClass().getComponentType(), length - diff); + if (diff < length) { + int end = length; + int dest = length - diff; + for (int i = indices.length - 1; i >= 0; i--) { + int index = indices[i]; + if (end - index > 1) { + int cp = end - index - 1; + dest -= cp; + System.arraycopy(array, index + 1, result, dest, cp); + } + end = index; + } + if (end > 0) { + System.arraycopy(array, 0, result, 0, end); + } + } + return result; + } + + /** + * Extract a set of Integer indices into an int[]. + * @param coll {@code HashSet} of {@code Integer} + * @return int[] + * @since 3.0.1 + */ + private static int[] extractIndices(HashSet<Integer> coll) { + int[] result = new int[coll.size()]; + int i = 0; + for (Integer index : coll) { + result[i++] = index.intValue(); + } + return result; + } }
\ No newline at end of file diff --git a/src/org/apache/commons/lang3/CharSequenceUtils.java b/src/org/apache/commons/lang3/CharSequenceUtils.java index e459699..a785ab7 100644 --- a/src/org/apache/commons/lang3/CharSequenceUtils.java +++ b/src/org/apache/commons/lang3/CharSequenceUtils.java @@ -1,197 +1,197 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-/**
- * <p>Operations on {@link java.lang.CharSequence} that are
- * {@code null} safe.</p>
- *
- * @see java.lang.CharSequence
- * @since 3.0
- * @version $Id$
- */
-public class CharSequenceUtils {
-
- /**
- * <p>{@code CharSequenceUtils} instances should NOT be constructed in
- * standard programming. </p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean
- * instance to operate.</p>
- */
- public CharSequenceUtils() {
- super();
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns a new {@code CharSequence} that is a subsequence of this
- * sequence starting with the {@code char} value at the specified index.</p>
- *
- * <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}.
- * The length (in {@code char}) of the returned sequence is {@code length() - start},
- * so if {@code start == end} then an empty sequence is returned.</p>
- *
- * @param cs the specified subsequence, null returns null
- * @param start the start index, inclusive, valid
- * @return a new subsequence, may be null
- * @throws IndexOutOfBoundsException if {@code start} is negative or if
- * {@code start} is greater than {@code length()}
- */
- public static CharSequence subSequence(CharSequence cs, int start) {
- return cs == null ? null : cs.subSequence(start, cs.length());
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the first index in the {@code CharSequence} that matches the
- * specified character.</p>
- *
- * @param cs the {@code CharSequence} to be processed, not null
- * @param searchChar the char to be searched for
- * @param start the start index, negative starts at the string start
- * @return the index where the search char was found, -1 if not found
- */
- static int indexOf(CharSequence cs, int searchChar, int start) {
- if (cs instanceof String) {
- return ((String) cs).indexOf(searchChar, start);
- } else {
- int sz = cs.length();
- if (start < 0) {
- start = 0;
- }
- for (int i = start; i < sz; i++) {
- if (cs.charAt(i) == searchChar) {
- return i;
- }
- }
- return -1;
- }
- }
-
- /**
- * Used by the indexOf(CharSequence methods) as a green implementation of indexOf.
- *
- * @param cs the {@code CharSequence} to be processed
- * @param searchChar the {@code CharSequence} to be searched for
- * @param start the start index
- * @return the index where the search sequence was found
- */
- static int indexOf(CharSequence cs, CharSequence searchChar, int start) {
- return cs.toString().indexOf(searchChar.toString(), start);
-// if (cs instanceof String && searchChar instanceof String) {
-// // TODO: Do we assume searchChar is usually relatively small;
-// // If so then calling toString() on it is better than reverting to
-// // the green implementation in the else block
-// return ((String) cs).indexOf((String) searchChar, start);
-// } else {
-// // TODO: Implement rather than convert to String
-// return cs.toString().indexOf(searchChar.toString(), start);
-// }
- }
-
- /**
- * <p>Finds the last index in the {@code CharSequence} that matches the
- * specified character.</p>
- *
- * @param cs the {@code CharSequence} to be processed
- * @param searchChar the char to be searched for
- * @param start the start index, negative returns -1, beyond length starts at end
- * @return the index where the search char was found, -1 if not found
- */
- static int lastIndexOf(CharSequence cs, int searchChar, int start) {
- if (cs instanceof String) {
- return ((String) cs).lastIndexOf(searchChar, start);
- } else {
- int sz = cs.length();
- if (start < 0) {
- return -1;
- }
- if (start >= sz) {
- start = sz - 1;
- }
- for (int i = start; i >= 0; --i) {
- if (cs.charAt(i) == searchChar) {
- return i;
- }
- }
- return -1;
- }
- }
-
- /**
- * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf
- *
- * @param cs the {@code CharSequence} to be processed
- * @param searchChar the {@code CharSequence} to be searched for
- * @param start the start index
- * @return the index where the search sequence was found
- */
- static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) {
- return cs.toString().lastIndexOf(searchChar.toString(), start);
-// if (cs instanceof String && searchChar instanceof String) {
-// // TODO: Do we assume searchChar is usually relatively small;
-// // If so then calling toString() on it is better than reverting to
-// // the green implementation in the else block
-// return ((String) cs).lastIndexOf((String) searchChar, start);
-// } else {
-// // TODO: Implement rather than convert to String
-// return cs.toString().lastIndexOf(searchChar.toString(), start);
-// }
- }
-
- /**
- * Green implementation of toCharArray.
- *
- * @param cs the {@code CharSequence} to be processed
- * @return the resulting char array
- */
- static char[] toCharArray(CharSequence cs) {
- if (cs instanceof String) {
- return ((String) cs).toCharArray();
- } else {
- int sz = cs.length();
- char[] array = new char[cs.length()];
- for (int i = 0; i < sz; i++) {
- array[i] = cs.charAt(i);
- }
- return array;
- }
- }
-
- /**
- * Green implementation of regionMatches.
- *
- * @param cs the {@code CharSequence} to be processed
- * @param ignoreCase whether or not to be case insensitive
- * @param thisStart the index to start on the {@code cs} CharSequence
- * @param substring the {@code CharSequence} to be looked for
- * @param start the index to start on the {@code substring} CharSequence
- * @param length character length of the region
- * @return whether the region matched
- */
- static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart,
- CharSequence substring, int start, int length) {
- if (cs instanceof String && substring instanceof String) {
- return ((String) cs).regionMatches(ignoreCase, thisStart, ((String) substring), start, length);
- } else {
- // TODO: Implement rather than convert to String
- return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start, length);
- }
- }
-
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + * <p>Operations on {@link java.lang.CharSequence} that are + * {@code null} safe.</p> + * + * @see java.lang.CharSequence + * @since 3.0 + * @version $Id$ + */ +public class CharSequenceUtils { + + /** + * <p>{@code CharSequenceUtils} instances should NOT be constructed in + * standard programming. </p> + * + * <p>This constructor is public to permit tools that require a JavaBean + * instance to operate.</p> + */ + public CharSequenceUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + * <p>Returns a new {@code CharSequence} that is a subsequence of this + * sequence starting with the {@code char} value at the specified index.</p> + * + * <p>This provides the {@code CharSequence} equivalent to {@link String#substring(int)}. + * The length (in {@code char}) of the returned sequence is {@code length() - start}, + * so if {@code start == end} then an empty sequence is returned.</p> + * + * @param cs the specified subsequence, null returns null + * @param start the start index, inclusive, valid + * @return a new subsequence, may be null + * @throws IndexOutOfBoundsException if {@code start} is negative or if + * {@code start} is greater than {@code length()} + */ + public static CharSequence subSequence(CharSequence cs, int start) { + return cs == null ? null : cs.subSequence(start, cs.length()); + } + + //----------------------------------------------------------------------- + /** + * <p>Finds the first index in the {@code CharSequence} that matches the + * specified character.</p> + * + * @param cs the {@code CharSequence} to be processed, not null + * @param searchChar the char to be searched for + * @param start the start index, negative starts at the string start + * @return the index where the search char was found, -1 if not found + */ + static int indexOf(CharSequence cs, int searchChar, int start) { + if (cs instanceof String) { + return ((String) cs).indexOf(searchChar, start); + } else { + int sz = cs.length(); + if (start < 0) { + start = 0; + } + for (int i = start; i < sz; i++) { + if (cs.charAt(i) == searchChar) { + return i; + } + } + return -1; + } + } + + /** + * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. + * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the {@code CharSequence} to be searched for + * @param start the start index + * @return the index where the search sequence was found + */ + static int indexOf(CharSequence cs, CharSequence searchChar, int start) { + return cs.toString().indexOf(searchChar.toString(), start); +// if (cs instanceof String && searchChar instanceof String) { +// // TODO: Do we assume searchChar is usually relatively small; +// // If so then calling toString() on it is better than reverting to +// // the green implementation in the else block +// return ((String) cs).indexOf((String) searchChar, start); +// } else { +// // TODO: Implement rather than convert to String +// return cs.toString().indexOf(searchChar.toString(), start); +// } + } + + /** + * <p>Finds the last index in the {@code CharSequence} that matches the + * specified character.</p> + * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the char to be searched for + * @param start the start index, negative returns -1, beyond length starts at end + * @return the index where the search char was found, -1 if not found + */ + static int lastIndexOf(CharSequence cs, int searchChar, int start) { + if (cs instanceof String) { + return ((String) cs).lastIndexOf(searchChar, start); + } else { + int sz = cs.length(); + if (start < 0) { + return -1; + } + if (start >= sz) { + start = sz - 1; + } + for (int i = start; i >= 0; --i) { + if (cs.charAt(i) == searchChar) { + return i; + } + } + return -1; + } + } + + /** + * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf + * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the {@code CharSequence} to be searched for + * @param start the start index + * @return the index where the search sequence was found + */ + static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) { + return cs.toString().lastIndexOf(searchChar.toString(), start); +// if (cs instanceof String && searchChar instanceof String) { +// // TODO: Do we assume searchChar is usually relatively small; +// // If so then calling toString() on it is better than reverting to +// // the green implementation in the else block +// return ((String) cs).lastIndexOf((String) searchChar, start); +// } else { +// // TODO: Implement rather than convert to String +// return cs.toString().lastIndexOf(searchChar.toString(), start); +// } + } + + /** + * Green implementation of toCharArray. + * + * @param cs the {@code CharSequence} to be processed + * @return the resulting char array + */ + static char[] toCharArray(CharSequence cs) { + if (cs instanceof String) { + return ((String) cs).toCharArray(); + } else { + int sz = cs.length(); + char[] array = new char[cs.length()]; + for (int i = 0; i < sz; i++) { + array[i] = cs.charAt(i); + } + return array; + } + } + + /** + * Green implementation of regionMatches. + * + * @param cs the {@code CharSequence} to be processed + * @param ignoreCase whether or not to be case insensitive + * @param thisStart the index to start on the {@code cs} CharSequence + * @param substring the {@code CharSequence} to be looked for + * @param start the index to start on the {@code substring} CharSequence + * @param length character length of the region + * @return whether the region matched + */ + static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart, + CharSequence substring, int start, int length) { + if (cs instanceof String && substring instanceof String) { + return ((String) cs).regionMatches(ignoreCase, thisStart, ((String) substring), start, length); + } else { + // TODO: Implement rather than convert to String + return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start, length); + } + } + }
\ No newline at end of file diff --git a/src/org/apache/commons/lang3/CharUtils.java b/src/org/apache/commons/lang3/CharUtils.java index e839e9b..e2ef207 100644 --- a/src/org/apache/commons/lang3/CharUtils.java +++ b/src/org/apache/commons/lang3/CharUtils.java @@ -1,518 +1,518 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-/**
- * <p>Operations on char primitives and Character objects.</p>
- *
- * <p>This class tries to handle {@code null} input gracefully.
- * An exception will not be thrown for a {@code null} input.
- * Each method documents its behaviour in more detail.</p>
- *
- * <p>#ThreadSafe#</p>
- * @since 2.1
- * @version $Id$
- */
-public class CharUtils {
-
- private static final String[] CHAR_STRING_ARRAY = new String[128];
-
- /**
- * {@code \u000a} linefeed LF ('\n').
- *
- * @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences
- * for Character and String Literals</a>
- * @since 2.2
- */
- public static final char LF = '\n';
-
- /**
- * {@code \u000d} carriage return CR ('\r').
- *
- * @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences
- * for Character and String Literals</a>
- * @since 2.2
- */
- public static final char CR = '\r';
-
-
- static {
- for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) {
- CHAR_STRING_ARRAY[c] = String.valueOf(c);
- }
- }
-
- /**
- * <p>{@code CharUtils} instances should NOT be constructed in standard programming.
- * Instead, the class should be used as {@code CharUtils.toString('c');}.</p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean instance
- * to operate.</p>
- */
- public CharUtils() {
- super();
- }
-
- /**
- * <p>Converts the String to a Character using the first character, returning
- * null for empty Strings.</p>
- *
- * <p>For ASCII 7 bit characters, this uses a cache that will return the
- * same Character object each time.</p>
- *
- * <pre>
- * CharUtils.toCharacterObject(null) = null
- * CharUtils.toCharacterObject("") = null
- * CharUtils.toCharacterObject("A") = 'A'
- * CharUtils.toCharacterObject("BA") = 'B'
- * </pre>
- *
- * @param str the character to convert
- * @return the Character value of the first letter of the String
- */
- public static Character toCharacterObject(String str) {
- if (StringUtils.isEmpty(str)) {
- return null;
- }
- return Character.valueOf(str.charAt(0));
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Converts the Character to a char throwing an exception for {@code null}.</p>
- *
- * <pre>
- * CharUtils.toChar(' ') = ' '
- * CharUtils.toChar('A') = 'A'
- * CharUtils.toChar(null) throws IllegalArgumentException
- * </pre>
- *
- * @param ch the character to convert
- * @return the char value of the Character
- * @throws IllegalArgumentException if the Character is null
- */
- public static char toChar(Character ch) {
- if (ch == null) {
- throw new IllegalArgumentException("The Character must not be null");
- }
- return ch.charValue();
- }
-
- /**
- * <p>Converts the Character to a char handling {@code null}.</p>
- *
- * <pre>
- * CharUtils.toChar(null, 'X') = 'X'
- * CharUtils.toChar(' ', 'X') = ' '
- * CharUtils.toChar('A', 'X') = 'A'
- * </pre>
- *
- * @param ch the character to convert
- * @param defaultValue the value to use if the Character is null
- * @return the char value of the Character or the default if null
- */
- public static char toChar(Character ch, char defaultValue) {
- if (ch == null) {
- return defaultValue;
- }
- return ch.charValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Converts the String to a char using the first character, throwing
- * an exception on empty Strings.</p>
- *
- * <pre>
- * CharUtils.toChar("A") = 'A'
- * CharUtils.toChar("BA") = 'B'
- * CharUtils.toChar(null) throws IllegalArgumentException
- * CharUtils.toChar("") throws IllegalArgumentException
- * </pre>
- *
- * @param str the character to convert
- * @return the char value of the first letter of the String
- * @throws IllegalArgumentException if the String is empty
- */
- public static char toChar(String str) {
- if (StringUtils.isEmpty(str)) {
- throw new IllegalArgumentException("The String must not be empty");
- }
- return str.charAt(0);
- }
-
- /**
- * <p>Converts the String to a char using the first character, defaulting
- * the value on empty Strings.</p>
- *
- * <pre>
- * CharUtils.toChar(null, 'X') = 'X'
- * CharUtils.toChar("", 'X') = 'X'
- * CharUtils.toChar("A", 'X') = 'A'
- * CharUtils.toChar("BA", 'X') = 'B'
- * </pre>
- *
- * @param str the character to convert
- * @param defaultValue the value to use if the Character is null
- * @return the char value of the first letter of the String or the default if null
- */
- public static char toChar(String str, char defaultValue) {
- if (StringUtils.isEmpty(str)) {
- return defaultValue;
- }
- return str.charAt(0);
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Converts the character to the Integer it represents, throwing an
- * exception if the character is not numeric.</p>
- *
- * <p>This method coverts the char '1' to the int 1 and so on.</p>
- *
- * <pre>
- * CharUtils.toIntValue('3') = 3
- * CharUtils.toIntValue('A') throws IllegalArgumentException
- * </pre>
- *
- * @param ch the character to convert
- * @return the int value of the character
- * @throws IllegalArgumentException if the character is not ASCII numeric
- */
- public static int toIntValue(char ch) {
- if (isAsciiNumeric(ch) == false) {
- throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'");
- }
- return ch - 48;
- }
-
- /**
- * <p>Converts the character to the Integer it represents, throwing an
- * exception if the character is not numeric.</p>
- *
- * <p>This method coverts the char '1' to the int 1 and so on.</p>
- *
- * <pre>
- * CharUtils.toIntValue('3', -1) = 3
- * CharUtils.toIntValue('A', -1) = -1
- * </pre>
- *
- * @param ch the character to convert
- * @param defaultValue the default value to use if the character is not numeric
- * @return the int value of the character
- */
- public static int toIntValue(char ch, int defaultValue) {
- if (isAsciiNumeric(ch) == false) {
- return defaultValue;
- }
- return ch - 48;
- }
-
- /**
- * <p>Converts the character to the Integer it represents, throwing an
- * exception if the character is not numeric.</p>
- *
- * <p>This method coverts the char '1' to the int 1 and so on.</p>
- *
- * <pre>
- * CharUtils.toIntValue('3') = 3
- * CharUtils.toIntValue(null) throws IllegalArgumentException
- * CharUtils.toIntValue('A') throws IllegalArgumentException
- * </pre>
- *
- * @param ch the character to convert, not null
- * @return the int value of the character
- * @throws IllegalArgumentException if the Character is not ASCII numeric or is null
- */
- public static int toIntValue(Character ch) {
- if (ch == null) {
- throw new IllegalArgumentException("The character must not be null");
- }
- return toIntValue(ch.charValue());
- }
-
- /**
- * <p>Converts the character to the Integer it represents, throwing an
- * exception if the character is not numeric.</p>
- *
- * <p>This method coverts the char '1' to the int 1 and so on.</p>
- *
- * <pre>
- * CharUtils.toIntValue(null, -1) = -1
- * CharUtils.toIntValue('3', -1) = 3
- * CharUtils.toIntValue('A', -1) = -1
- * </pre>
- *
- * @param ch the character to convert
- * @param defaultValue the default value to use if the character is not numeric
- * @return the int value of the character
- */
- public static int toIntValue(Character ch, int defaultValue) {
- if (ch == null) {
- return defaultValue;
- }
- return toIntValue(ch.charValue(), defaultValue);
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Converts the character to a String that contains the one character.</p>
- *
- * <p>For ASCII 7 bit characters, this uses a cache that will return the
- * same String object each time.</p>
- *
- * <pre>
- * CharUtils.toString(' ') = " "
- * CharUtils.toString('A') = "A"
- * </pre>
- *
- * @param ch the character to convert
- * @return a String containing the one specified character
- */
- public static String toString(char ch) {
- if (ch < 128) {
- return CHAR_STRING_ARRAY[ch];
- }
- return new String(new char[] {ch});
- }
-
- /**
- * <p>Converts the character to a String that contains the one character.</p>
- *
- * <p>For ASCII 7 bit characters, this uses a cache that will return the
- * same String object each time.</p>
- *
- * <p>If {@code null} is passed in, {@code null} will be returned.</p>
- *
- * <pre>
- * CharUtils.toString(null) = null
- * CharUtils.toString(' ') = " "
- * CharUtils.toString('A') = "A"
- * </pre>
- *
- * @param ch the character to convert
- * @return a String containing the one specified character
- */
- public static String toString(Character ch) {
- if (ch == null) {
- return null;
- }
- return toString(ch.charValue());
- }
-
- //--------------------------------------------------------------------------
- /**
- * <p>Converts the string to the Unicode format '\u0020'.</p>
- *
- * <p>This format is the Java source code format.</p>
- *
- * <pre>
- * CharUtils.unicodeEscaped(' ') = "\u0020"
- * CharUtils.unicodeEscaped('A') = "\u0041"
- * </pre>
- *
- * @param ch the character to convert
- * @return the escaped Unicode string
- */
- public static String unicodeEscaped(char ch) {
- if (ch < 0x10) {
- return "\\u000" + Integer.toHexString(ch);
- } else if (ch < 0x100) {
- return "\\u00" + Integer.toHexString(ch);
- } else if (ch < 0x1000) {
- return "\\u0" + Integer.toHexString(ch);
- }
- return "\\u" + Integer.toHexString(ch);
- }
-
- /**
- * <p>Converts the string to the Unicode format '\u0020'.</p>
- *
- * <p>This format is the Java source code format.</p>
- *
- * <p>If {@code null} is passed in, {@code null} will be returned.</p>
- *
- * <pre>
- * CharUtils.unicodeEscaped(null) = null
- * CharUtils.unicodeEscaped(' ') = "\u0020"
- * CharUtils.unicodeEscaped('A') = "\u0041"
- * </pre>
- *
- * @param ch the character to convert, may be null
- * @return the escaped Unicode string, null if null input
- */
- public static String unicodeEscaped(Character ch) {
- if (ch == null) {
- return null;
- }
- return unicodeEscaped(ch.charValue());
- }
-
- //--------------------------------------------------------------------------
- /**
- * <p>Checks whether the character is ASCII 7 bit.</p>
- *
- * <pre>
- * CharUtils.isAscii('a') = true
- * CharUtils.isAscii('A') = true
- * CharUtils.isAscii('3') = true
- * CharUtils.isAscii('-') = true
- * CharUtils.isAscii('\n') = true
- * CharUtils.isAscii('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if less than 128
- */
- public static boolean isAscii(char ch) {
- return ch < 128;
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit printable.</p>
- *
- * <pre>
- * CharUtils.isAsciiPrintable('a') = true
- * CharUtils.isAsciiPrintable('A') = true
- * CharUtils.isAsciiPrintable('3') = true
- * CharUtils.isAsciiPrintable('-') = true
- * CharUtils.isAsciiPrintable('\n') = false
- * CharUtils.isAsciiPrintable('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 32 and 126 inclusive
- */
- public static boolean isAsciiPrintable(char ch) {
- return ch >= 32 && ch < 127;
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit control.</p>
- *
- * <pre>
- * CharUtils.isAsciiControl('a') = false
- * CharUtils.isAsciiControl('A') = false
- * CharUtils.isAsciiControl('3') = false
- * CharUtils.isAsciiControl('-') = false
- * CharUtils.isAsciiControl('\n') = true
- * CharUtils.isAsciiControl('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if less than 32 or equals 127
- */
- public static boolean isAsciiControl(char ch) {
- return ch < 32 || ch == 127;
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit alphabetic.</p>
- *
- * <pre>
- * CharUtils.isAsciiAlpha('a') = true
- * CharUtils.isAsciiAlpha('A') = true
- * CharUtils.isAsciiAlpha('3') = false
- * CharUtils.isAsciiAlpha('-') = false
- * CharUtils.isAsciiAlpha('\n') = false
- * CharUtils.isAsciiAlpha('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 65 and 90 or 97 and 122 inclusive
- */
- public static boolean isAsciiAlpha(char ch) {
- return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z');
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit alphabetic upper case.</p>
- *
- * <pre>
- * CharUtils.isAsciiAlphaUpper('a') = false
- * CharUtils.isAsciiAlphaUpper('A') = true
- * CharUtils.isAsciiAlphaUpper('3') = false
- * CharUtils.isAsciiAlphaUpper('-') = false
- * CharUtils.isAsciiAlphaUpper('\n') = false
- * CharUtils.isAsciiAlphaUpper('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 65 and 90 inclusive
- */
- public static boolean isAsciiAlphaUpper(char ch) {
- return ch >= 'A' && ch <= 'Z';
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit alphabetic lower case.</p>
- *
- * <pre>
- * CharUtils.isAsciiAlphaLower('a') = true
- * CharUtils.isAsciiAlphaLower('A') = false
- * CharUtils.isAsciiAlphaLower('3') = false
- * CharUtils.isAsciiAlphaLower('-') = false
- * CharUtils.isAsciiAlphaLower('\n') = false
- * CharUtils.isAsciiAlphaLower('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 97 and 122 inclusive
- */
- public static boolean isAsciiAlphaLower(char ch) {
- return ch >= 'a' && ch <= 'z';
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit numeric.</p>
- *
- * <pre>
- * CharUtils.isAsciiNumeric('a') = false
- * CharUtils.isAsciiNumeric('A') = false
- * CharUtils.isAsciiNumeric('3') = true
- * CharUtils.isAsciiNumeric('-') = false
- * CharUtils.isAsciiNumeric('\n') = false
- * CharUtils.isAsciiNumeric('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 48 and 57 inclusive
- */
- public static boolean isAsciiNumeric(char ch) {
- return ch >= '0' && ch <= '9';
- }
-
- /**
- * <p>Checks whether the character is ASCII 7 bit numeric.</p>
- *
- * <pre>
- * CharUtils.isAsciiAlphanumeric('a') = true
- * CharUtils.isAsciiAlphanumeric('A') = true
- * CharUtils.isAsciiAlphanumeric('3') = true
- * CharUtils.isAsciiAlphanumeric('-') = false
- * CharUtils.isAsciiAlphanumeric('\n') = false
- * CharUtils.isAsciiAlphanumeric('©') = false
- * </pre>
- *
- * @param ch the character to check
- * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive
- */
- public static boolean isAsciiAlphanumeric(char ch) {
- return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9');
- }
-
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + * <p>Operations on char primitives and Character objects.</p> + * + * <p>This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.</p> + * + * <p>#ThreadSafe#</p> + * @since 2.1 + * @version $Id$ + */ +public class CharUtils { + + private static final String[] CHAR_STRING_ARRAY = new String[128]; + + /** + * {@code \u000a} linefeed LF ('\n'). + * + * @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences + * for Character and String Literals</a> + * @since 2.2 + */ + public static final char LF = '\n'; + + /** + * {@code \u000d} carriage return CR ('\r'). + * + * @see <a href="http://java.sun.com/docs/books/jls/third_edition/html/lexical.html#101089">JLF: Escape Sequences + * for Character and String Literals</a> + * @since 2.2 + */ + public static final char CR = '\r'; + + + static { + for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) { + CHAR_STRING_ARRAY[c] = String.valueOf(c); + } + } + + /** + * <p>{@code CharUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code CharUtils.toString('c');}.</p> + * + * <p>This constructor is public to permit tools that require a JavaBean instance + * to operate.</p> + */ + public CharUtils() { + super(); + } + + /** + * <p>Converts the String to a Character using the first character, returning + * null for empty Strings.</p> + * + * <p>For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.</p> + * + * <pre> + * CharUtils.toCharacterObject(null) = null + * CharUtils.toCharacterObject("") = null + * CharUtils.toCharacterObject("A") = 'A' + * CharUtils.toCharacterObject("BA") = 'B' + * </pre> + * + * @param str the character to convert + * @return the Character value of the first letter of the String + */ + public static Character toCharacterObject(String str) { + if (StringUtils.isEmpty(str)) { + return null; + } + return Character.valueOf(str.charAt(0)); + } + + //----------------------------------------------------------------------- + /** + * <p>Converts the Character to a char throwing an exception for {@code null}.</p> + * + * <pre> + * CharUtils.toChar(' ') = ' ' + * CharUtils.toChar('A') = 'A' + * CharUtils.toChar(null) throws IllegalArgumentException + * </pre> + * + * @param ch the character to convert + * @return the char value of the Character + * @throws IllegalArgumentException if the Character is null + */ + public static char toChar(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The Character must not be null"); + } + return ch.charValue(); + } + + /** + * <p>Converts the Character to a char handling {@code null}.</p> + * + * <pre> + * CharUtils.toChar(null, 'X') = 'X' + * CharUtils.toChar(' ', 'X') = ' ' + * CharUtils.toChar('A', 'X') = 'A' + * </pre> + * + * @param ch the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the Character or the default if null + */ + public static char toChar(Character ch, char defaultValue) { + if (ch == null) { + return defaultValue; + } + return ch.charValue(); + } + + //----------------------------------------------------------------------- + /** + * <p>Converts the String to a char using the first character, throwing + * an exception on empty Strings.</p> + * + * <pre> + * CharUtils.toChar("A") = 'A' + * CharUtils.toChar("BA") = 'B' + * CharUtils.toChar(null) throws IllegalArgumentException + * CharUtils.toChar("") throws IllegalArgumentException + * </pre> + * + * @param str the character to convert + * @return the char value of the first letter of the String + * @throws IllegalArgumentException if the String is empty + */ + public static char toChar(String str) { + if (StringUtils.isEmpty(str)) { + throw new IllegalArgumentException("The String must not be empty"); + } + return str.charAt(0); + } + + /** + * <p>Converts the String to a char using the first character, defaulting + * the value on empty Strings.</p> + * + * <pre> + * CharUtils.toChar(null, 'X') = 'X' + * CharUtils.toChar("", 'X') = 'X' + * CharUtils.toChar("A", 'X') = 'A' + * CharUtils.toChar("BA", 'X') = 'B' + * </pre> + * + * @param str the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the first letter of the String or the default if null + */ + public static char toChar(String str, char defaultValue) { + if (StringUtils.isEmpty(str)) { + return defaultValue; + } + return str.charAt(0); + } + + //----------------------------------------------------------------------- + /** + * <p>Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.</p> + * + * <p>This method coverts the char '1' to the int 1 and so on.</p> + * + * <pre> + * CharUtils.toIntValue('3') = 3 + * CharUtils.toIntValue('A') throws IllegalArgumentException + * </pre> + * + * @param ch the character to convert + * @return the int value of the character + * @throws IllegalArgumentException if the character is not ASCII numeric + */ + public static int toIntValue(char ch) { + if (isAsciiNumeric(ch) == false) { + throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'"); + } + return ch - 48; + } + + /** + * <p>Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.</p> + * + * <p>This method coverts the char '1' to the int 1 and so on.</p> + * + * <pre> + * CharUtils.toIntValue('3', -1) = 3 + * CharUtils.toIntValue('A', -1) = -1 + * </pre> + * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(char ch, int defaultValue) { + if (isAsciiNumeric(ch) == false) { + return defaultValue; + } + return ch - 48; + } + + /** + * <p>Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.</p> + * + * <p>This method coverts the char '1' to the int 1 and so on.</p> + * + * <pre> + * CharUtils.toIntValue('3') = 3 + * CharUtils.toIntValue(null) throws IllegalArgumentException + * CharUtils.toIntValue('A') throws IllegalArgumentException + * </pre> + * + * @param ch the character to convert, not null + * @return the int value of the character + * @throws IllegalArgumentException if the Character is not ASCII numeric or is null + */ + public static int toIntValue(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The character must not be null"); + } + return toIntValue(ch.charValue()); + } + + /** + * <p>Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.</p> + * + * <p>This method coverts the char '1' to the int 1 and so on.</p> + * + * <pre> + * CharUtils.toIntValue(null, -1) = -1 + * CharUtils.toIntValue('3', -1) = 3 + * CharUtils.toIntValue('A', -1) = -1 + * </pre> + * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(Character ch, int defaultValue) { + if (ch == null) { + return defaultValue; + } + return toIntValue(ch.charValue(), defaultValue); + } + + //----------------------------------------------------------------------- + /** + * <p>Converts the character to a String that contains the one character.</p> + * + * <p>For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.</p> + * + * <pre> + * CharUtils.toString(' ') = " " + * CharUtils.toString('A') = "A" + * </pre> + * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(char ch) { + if (ch < 128) { + return CHAR_STRING_ARRAY[ch]; + } + return new String(new char[] {ch}); + } + + /** + * <p>Converts the character to a String that contains the one character.</p> + * + * <p>For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.</p> + * + * <p>If {@code null} is passed in, {@code null} will be returned.</p> + * + * <pre> + * CharUtils.toString(null) = null + * CharUtils.toString(' ') = " " + * CharUtils.toString('A') = "A" + * </pre> + * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(Character ch) { + if (ch == null) { + return null; + } + return toString(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + * <p>Converts the string to the Unicode format '\u0020'.</p> + * + * <p>This format is the Java source code format.</p> + * + * <pre> + * CharUtils.unicodeEscaped(' ') = "\u0020" + * CharUtils.unicodeEscaped('A') = "\u0041" + * </pre> + * + * @param ch the character to convert + * @return the escaped Unicode string + */ + public static String unicodeEscaped(char ch) { + if (ch < 0x10) { + return "\\u000" + Integer.toHexString(ch); + } else if (ch < 0x100) { + return "\\u00" + Integer.toHexString(ch); + } else if (ch < 0x1000) { + return "\\u0" + Integer.toHexString(ch); + } + return "\\u" + Integer.toHexString(ch); + } + + /** + * <p>Converts the string to the Unicode format '\u0020'.</p> + * + * <p>This format is the Java source code format.</p> + * + * <p>If {@code null} is passed in, {@code null} will be returned.</p> + * + * <pre> + * CharUtils.unicodeEscaped(null) = null + * CharUtils.unicodeEscaped(' ') = "\u0020" + * CharUtils.unicodeEscaped('A') = "\u0041" + * </pre> + * + * @param ch the character to convert, may be null + * @return the escaped Unicode string, null if null input + */ + public static String unicodeEscaped(Character ch) { + if (ch == null) { + return null; + } + return unicodeEscaped(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + * <p>Checks whether the character is ASCII 7 bit.</p> + * + * <pre> + * CharUtils.isAscii('a') = true + * CharUtils.isAscii('A') = true + * CharUtils.isAscii('3') = true + * CharUtils.isAscii('-') = true + * CharUtils.isAscii('\n') = true + * CharUtils.isAscii('©') = false + * </pre> + * + * @param ch the character to check + * @return true if less than 128 + */ + public static boolean isAscii(char ch) { + return ch < 128; + } + + /** + * <p>Checks whether the character is ASCII 7 bit printable.</p> + * + * <pre> + * CharUtils.isAsciiPrintable('a') = true + * CharUtils.isAsciiPrintable('A') = true + * CharUtils.isAsciiPrintable('3') = true + * CharUtils.isAsciiPrintable('-') = true + * CharUtils.isAsciiPrintable('\n') = false + * CharUtils.isAsciiPrintable('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 32 and 126 inclusive + */ + public static boolean isAsciiPrintable(char ch) { + return ch >= 32 && ch < 127; + } + + /** + * <p>Checks whether the character is ASCII 7 bit control.</p> + * + * <pre> + * CharUtils.isAsciiControl('a') = false + * CharUtils.isAsciiControl('A') = false + * CharUtils.isAsciiControl('3') = false + * CharUtils.isAsciiControl('-') = false + * CharUtils.isAsciiControl('\n') = true + * CharUtils.isAsciiControl('©') = false + * </pre> + * + * @param ch the character to check + * @return true if less than 32 or equals 127 + */ + public static boolean isAsciiControl(char ch) { + return ch < 32 || ch == 127; + } + + /** + * <p>Checks whether the character is ASCII 7 bit alphabetic.</p> + * + * <pre> + * CharUtils.isAsciiAlpha('a') = true + * CharUtils.isAsciiAlpha('A') = true + * CharUtils.isAsciiAlpha('3') = false + * CharUtils.isAsciiAlpha('-') = false + * CharUtils.isAsciiAlpha('\n') = false + * CharUtils.isAsciiAlpha('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlpha(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + + /** + * <p>Checks whether the character is ASCII 7 bit alphabetic upper case.</p> + * + * <pre> + * CharUtils.isAsciiAlphaUpper('a') = false + * CharUtils.isAsciiAlphaUpper('A') = true + * CharUtils.isAsciiAlphaUpper('3') = false + * CharUtils.isAsciiAlphaUpper('-') = false + * CharUtils.isAsciiAlphaUpper('\n') = false + * CharUtils.isAsciiAlphaUpper('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 65 and 90 inclusive + */ + public static boolean isAsciiAlphaUpper(char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + * <p>Checks whether the character is ASCII 7 bit alphabetic lower case.</p> + * + * <pre> + * CharUtils.isAsciiAlphaLower('a') = true + * CharUtils.isAsciiAlphaLower('A') = false + * CharUtils.isAsciiAlphaLower('3') = false + * CharUtils.isAsciiAlphaLower('-') = false + * CharUtils.isAsciiAlphaLower('\n') = false + * CharUtils.isAsciiAlphaLower('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 97 and 122 inclusive + */ + public static boolean isAsciiAlphaLower(char ch) { + return ch >= 'a' && ch <= 'z'; + } + + /** + * <p>Checks whether the character is ASCII 7 bit numeric.</p> + * + * <pre> + * CharUtils.isAsciiNumeric('a') = false + * CharUtils.isAsciiNumeric('A') = false + * CharUtils.isAsciiNumeric('3') = true + * CharUtils.isAsciiNumeric('-') = false + * CharUtils.isAsciiNumeric('\n') = false + * CharUtils.isAsciiNumeric('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 48 and 57 inclusive + */ + public static boolean isAsciiNumeric(char ch) { + return ch >= '0' && ch <= '9'; + } + + /** + * <p>Checks whether the character is ASCII 7 bit numeric.</p> + * + * <pre> + * CharUtils.isAsciiAlphanumeric('a') = true + * CharUtils.isAsciiAlphanumeric('A') = true + * CharUtils.isAsciiAlphanumeric('3') = true + * CharUtils.isAsciiAlphanumeric('-') = false + * CharUtils.isAsciiAlphanumeric('\n') = false + * CharUtils.isAsciiAlphanumeric('©') = false + * </pre> + * + * @param ch the character to check + * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlphanumeric(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + } + }
\ No newline at end of file diff --git a/src/org/apache/commons/lang3/ClassUtils.java b/src/org/apache/commons/lang3/ClassUtils.java index 032cc6d..48910f8 100644 --- a/src/org/apache/commons/lang3/ClassUtils.java +++ b/src/org/apache/commons/lang3/ClassUtils.java @@ -1,1103 +1,1103 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Map;
-
-
-/**
- * <p>Operates on classes without using reflection.</p>
- *
- * <p>This class handles invalid {@code null} inputs as best it can.
- * Each method documents its behaviour in more detail.</p>
- *
- * <p>The notion of a {@code canonical name} includes the human
- * readable name for the type, for example {@code int[]}. The
- * non-canonical method variants work with the JVM names, such as
- * {@code [I}. </p>
- *
- * @since 2.0
- * @version $Id: ClassUtils.java 1145035 2011-07-11 06:09:39Z bayard $
- */
-public class ClassUtils {
-
- /**
- * <p>The package separator character: <code>'.' == {@value}</code>.</p>
- */
- public static final char PACKAGE_SEPARATOR_CHAR = '.';
-
- /**
- * <p>The package separator String: {@code "."}.</p>
- */
- public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR);
-
- /**
- * <p>The inner class separator character: <code>'$' == {@value}</code>.</p>
- */
- public static final char INNER_CLASS_SEPARATOR_CHAR = '$';
-
- /**
- * <p>The inner class separator String: {@code "$"}.</p>
- */
- public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR);
-
- /**
- * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}.
- */
- private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>();
- static {
- primitiveWrapperMap.put(Boolean.TYPE, Boolean.class);
- primitiveWrapperMap.put(Byte.TYPE, Byte.class);
- primitiveWrapperMap.put(Character.TYPE, Character.class);
- primitiveWrapperMap.put(Short.TYPE, Short.class);
- primitiveWrapperMap.put(Integer.TYPE, Integer.class);
- primitiveWrapperMap.put(Long.TYPE, Long.class);
- primitiveWrapperMap.put(Double.TYPE, Double.class);
- primitiveWrapperMap.put(Float.TYPE, Float.class);
- primitiveWrapperMap.put(Void.TYPE, Void.TYPE);
- }
-
- /**
- * Maps wrapper {@code Class}es to their corresponding primitive types.
- */
- private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>();
- static {
- for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) {
- Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass);
- if (!primitiveClass.equals(wrapperClass)) {
- wrapperPrimitiveMap.put(wrapperClass, primitiveClass);
- }
- }
- }
-
- /**
- * Maps a primitive class name to its corresponding abbreviation used in array class names.
- */
- private static final Map<String, String> abbreviationMap = new HashMap<String, String>();
-
- /**
- * Maps an abbreviation used in array class names to corresponding primitive class name.
- */
- private static final Map<String, String> reverseAbbreviationMap = new HashMap<String, String>();
-
- /**
- * Add primitive type abbreviation to maps of abbreviations.
- *
- * @param primitive Canonical name of primitive type
- * @param abbreviation Corresponding abbreviation of primitive type
- */
- private static void addAbbreviation(String primitive, String abbreviation) {
- abbreviationMap.put(primitive, abbreviation);
- reverseAbbreviationMap.put(abbreviation, primitive);
- }
-
- /**
- * Feed abbreviation maps
- */
- static {
- addAbbreviation("int", "I");
- addAbbreviation("boolean", "Z");
- addAbbreviation("float", "F");
- addAbbreviation("long", "J");
- addAbbreviation("short", "S");
- addAbbreviation("byte", "B");
- addAbbreviation("double", "D");
- addAbbreviation("char", "C");
- }
-
- /**
- * <p>ClassUtils instances should NOT be constructed in standard programming.
- * Instead, the class should be used as
- * {@code ClassUtils.getShortClassName(cls)}.</p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean
- * instance to operate.</p>
- */
- public ClassUtils() {
- super();
- }
-
- // Short class name
- // ----------------------------------------------------------------------
- /**
- * <p>Gets the class name minus the package name for an {@code Object}.</p>
- *
- * @param object the class to get the short name for, may be null
- * @param valueIfNull the value to return if null
- * @return the class name of the object without the package name, or the null value
- */
- public static String getShortClassName(Object object, String valueIfNull) {
- if (object == null) {
- return valueIfNull;
- }
- return getShortClassName(object.getClass());
- }
-
- /**
- * <p>Gets the class name minus the package name from a {@code Class}.</p>
- *
- * <p>Consider using the Java 5 API {@link Class#getSimpleName()} instead.
- * The one known difference is that this code will return {@code "Map.Entry"} while
- * the {@code java.lang.Class} variant will simply return {@code "Entry"}. </p>
- *
- * @param cls the class to get the short name for.
- * @return the class name without the package name or an empty string
- */
- public static String getShortClassName(Class<?> cls) {
- if (cls == null) {
- return StringUtils.EMPTY;
- }
- return getShortClassName(cls.getName());
- }
-
- /**
- * <p>Gets the class name minus the package name from a String.</p>
- *
- * <p>The string passed in is assumed to be a class name - it is not checked.</p>
-
- * <p>Note that this method differs from Class.getSimpleName() in that this will
- * return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply
- * return {@code "Entry"}. </p>
- *
- * @param className the className to get the short name for
- * @return the class name of the class without the package name or an empty string
- */
- public static String getShortClassName(String className) {
- if (className == null) {
- return StringUtils.EMPTY;
- }
- if (className.length() == 0) {
- return StringUtils.EMPTY;
- }
-
- StringBuilder arrayPrefix = new StringBuilder();
-
- // Handle array encoding
- if (className.startsWith("[")) {
- while (className.charAt(0) == '[') {
- className = className.substring(1);
- arrayPrefix.append("[]");
- }
- // Strip Object type encoding
- if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
- className = className.substring(1, className.length() - 1);
- }
- }
-
- if (reverseAbbreviationMap.containsKey(className)) {
- className = reverseAbbreviationMap.get(className);
- }
-
- int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
- int innerIdx = className.indexOf(
- INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1);
- String out = className.substring(lastDotIdx + 1);
- if (innerIdx != -1) {
- out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR);
- }
- return out + arrayPrefix;
- }
-
- /**
- * <p>Null-safe version of <code>aClass.getSimpleName()</code></p>
- *
- * @param cls the class for which to get the simple name.
- * @return the simple class name.
- * @since 3.0
- * @see Class#getSimpleName()
- */
- public static String getSimpleName(Class<?> cls) {
- if (cls == null) {
- return StringUtils.EMPTY;
- }
- return cls.getSimpleName();
- }
-
- /**
- * <p>Null-safe version of <code>aClass.getSimpleName()</code></p>
- *
- * @param object the object for which to get the simple class name.
- * @param valueIfNull the value to return if <code>object</code> is <code>null</code>
- * @return the simple class name.
- * @since 3.0
- * @see Class#getSimpleName()
- */
- public static String getSimpleName(Object object, String valueIfNull) {
- if (object == null) {
- return valueIfNull;
- }
- return getSimpleName(object.getClass());
- }
-
- // Package name
- // ----------------------------------------------------------------------
- /**
- * <p>Gets the package name of an {@code Object}.</p>
- *
- * @param object the class to get the package name for, may be null
- * @param valueIfNull the value to return if null
- * @return the package name of the object, or the null value
- */
- public static String getPackageName(Object object, String valueIfNull) {
- if (object == null) {
- return valueIfNull;
- }
- return getPackageName(object.getClass());
- }
-
- /**
- * <p>Gets the package name of a {@code Class}.</p>
- *
- * @param cls the class to get the package name for, may be {@code null}.
- * @return the package name or an empty string
- */
- public static String getPackageName(Class<?> cls) {
- if (cls == null) {
- return StringUtils.EMPTY;
- }
- return getPackageName(cls.getName());
- }
-
- /**
- * <p>Gets the package name from a {@code String}.</p>
- *
- * <p>The string passed in is assumed to be a class name - it is not checked.</p>
- * <p>If the class is unpackaged, return an empty string.</p>
- *
- * @param className the className to get the package name for, may be {@code null}
- * @return the package name or an empty string
- */
- public static String getPackageName(String className) {
- if (className == null || className.length() == 0) {
- return StringUtils.EMPTY;
- }
-
- // Strip array encoding
- while (className.charAt(0) == '[') {
- className = className.substring(1);
- }
- // Strip Object type encoding
- if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') {
- className = className.substring(1);
- }
-
- int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
- if (i == -1) {
- return StringUtils.EMPTY;
- }
- return className.substring(0, i);
- }
-
- // Superclasses/Superinterfaces
- // ----------------------------------------------------------------------
- /**
- * <p>Gets a {@code List} of superclasses for the given class.</p>
- *
- * @param cls the class to look up, may be {@code null}
- * @return the {@code List} of superclasses in order going up from this one
- * {@code null} if null input
- */
- public static List<Class<?>> getAllSuperclasses(Class<?> cls) {
- if (cls == null) {
- return null;
- }
- List<Class<?>> classes = new ArrayList<Class<?>>();
- Class<?> superclass = cls.getSuperclass();
- while (superclass != null) {
- classes.add(superclass);
- superclass = superclass.getSuperclass();
- }
- return classes;
- }
-
- /**
- * <p>Gets a {@code List} of all interfaces implemented by the given
- * class and its superclasses.</p>
- *
- * <p>The order is determined by looking through each interface in turn as
- * declared in the source file and following its hierarchy up. Then each
- * superclass is considered in the same way. Later duplicates are ignored,
- * so the order is maintained.</p>
- *
- * @param cls the class to look up, may be {@code null}
- * @return the {@code List} of interfaces in order,
- * {@code null} if null input
- */
- public static List<Class<?>> getAllInterfaces(Class<?> cls) {
- if (cls == null) {
- return null;
- }
-
- LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>();
- getAllInterfaces(cls, interfacesFound);
-
- return new ArrayList<Class<?>>(interfacesFound);
- }
-
- /**
- * Get the interfaces for the specified class.
- *
- * @param cls the class to look up, may be {@code null}
- * @param interfacesFound the {@code Set} of interfaces for the class
- */
- private static void getAllInterfaces(Class<?> cls, HashSet<Class<?>> interfacesFound) {
- while (cls != null) {
- Class<?>[] interfaces = cls.getInterfaces();
-
- for (Class<?> i : interfaces) {
- if (interfacesFound.add(i)) {
- getAllInterfaces(i, interfacesFound);
- }
- }
-
- cls = cls.getSuperclass();
- }
- }
-
- // Convert list
- // ----------------------------------------------------------------------
- /**
- * <p>Given a {@code List} of class names, this method converts them into classes.</p>
- *
- * <p>A new {@code List} is returned. If the class name cannot be found, {@code null}
- * is stored in the {@code List}. If the class name in the {@code List} is
- * {@code null}, {@code null} is stored in the output {@code List}.</p>
- *
- * @param classNames the classNames to change
- * @return a {@code List} of Class objects corresponding to the class names,
- * {@code null} if null input
- * @throws ClassCastException if classNames contains a non String entry
- */
- public static List<Class<?>> convertClassNamesToClasses(List<String> classNames) {
- if (classNames == null) {
- return null;
- }
- List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size());
- for (String className : classNames) {
- try {
- classes.add(Class.forName(className));
- } catch (Exception ex) {
- classes.add(null);
- }
- }
- return classes;
- }
-
- /**
- * <p>Given a {@code List} of {@code Class} objects, this method converts
- * them into class names.</p>
- *
- * <p>A new {@code List} is returned. {@code null} objects will be copied into
- * the returned list as {@code null}.</p>
- *
- * @param classes the classes to change
- * @return a {@code List} of class names corresponding to the Class objects,
- * {@code null} if null input
- * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry
- */
- public static List<String> convertClassesToClassNames(List<Class<?>> classes) {
- if (classes == null) {
- return null;
- }
- List<String> classNames = new ArrayList<String>(classes.size());
- for (Class<?> cls : classes) {
- if (cls == null) {
- classNames.add(null);
- } else {
- classNames.add(cls.getName());
- }
- }
- return classNames;
- }
-
- // Is assignable
- // ----------------------------------------------------------------------
- /**
- * <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
- *
- * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
- * Class pair in the input arrays. It can be used to check if a set of arguments
- * (the first parameter) are suitably compatible with a set of method parameter types
- * (the second parameter).</p>
- *
- * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
- * method takes into account widenings of primitive classes and
- * {@code null}s.</p>
- *
- * <p>Primitive widenings allow an int to be assigned to a {@code long},
- * {@code float} or {@code double}. This method returns the correct
- * result for these cases.</p>
- *
- * <p>{@code Null} may be assigned to any reference type. This method will
- * return {@code true} if {@code null} is passed in and the toClass is
- * non-primitive.</p>
- *
- * <p>Specifically, this method tests whether the type represented by the
- * specified {@code Class} parameter can be converted to the type
- * represented by this {@code Class} object via an identity conversion
- * widening primitive or widening reference conversion. See
- * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
- * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
- *
- * <p><strong>Since Lang 3.0,</strong> this method will default behavior for
- * calculating assignability between primitive and wrapper types <em>corresponding
- * to the running Java version</em>; i.e. autoboxing will be the default
- * behavior in VMs running Java versions >= 1.5.</p>
- *
- * @param classArray the array of Classes to check, may be {@code null}
- * @param toClassArray the array of Classes to try to assign into, may be {@code null}
- * @return {@code true} if assignment possible
- */
- public static boolean isAssignable(Class<?>[] classArray, Class<?>... toClassArray) {
- return isAssignable(classArray, toClassArray, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5));
- }
-
- /**
- * <p>Checks if an array of Classes can be assigned to another array of Classes.</p>
- *
- * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each
- * Class pair in the input arrays. It can be used to check if a set of arguments
- * (the first parameter) are suitably compatible with a set of method parameter types
- * (the second parameter).</p>
- *
- * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this
- * method takes into account widenings of primitive classes and
- * {@code null}s.</p>
- *
- * <p>Primitive widenings allow an int to be assigned to a {@code long},
- * {@code float} or {@code double}. This method returns the correct
- * result for these cases.</p>
- *
- * <p>{@code Null} may be assigned to any reference type. This method will
- * return {@code true} if {@code null} is passed in and the toClass is
- * non-primitive.</p>
- *
- * <p>Specifically, this method tests whether the type represented by the
- * specified {@code Class} parameter can be converted to the type
- * represented by this {@code Class} object via an identity conversion
- * widening primitive or widening reference conversion. See
- * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
- * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
- *
- * @param classArray the array of Classes to check, may be {@code null}
- * @param toClassArray the array of Classes to try to assign into, may be {@code null}
- * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
- * @return {@code true} if assignment possible
- */
- public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray, boolean autoboxing) {
- if (ArrayUtils.isSameLength(classArray, toClassArray) == false) {
- return false;
- }
- if (classArray == null) {
- classArray = ArrayUtils.EMPTY_CLASS_ARRAY;
- }
- if (toClassArray == null) {
- toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY;
- }
- for (int i = 0; i < classArray.length; i++) {
- if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if one {@code Class} can be assigned to a variable of
- * another {@code Class}.</p>
- *
- * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
- * this method takes into account widenings of primitive classes and
- * {@code null}s.</p>
- *
- * <p>Primitive widenings allow an int to be assigned to a long, float or
- * double. This method returns the correct result for these cases.</p>
- *
- * <p>{@code Null} may be assigned to any reference type. This method
- * will return {@code true} if {@code null} is passed in and the
- * toClass is non-primitive.</p>
- *
- * <p>Specifically, this method tests whether the type represented by the
- * specified {@code Class} parameter can be converted to the type
- * represented by this {@code Class} object via an identity conversion
- * widening primitive or widening reference conversion. See
- * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
- * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
- *
- * <p><strong>Since Lang 3.0,</strong> this method will default behavior for
- * calculating assignability between primitive and wrapper types <em>corresponding
- * to the running Java version</em>; i.e. autoboxing will be the default
- * behavior in VMs running Java versions >= 1.5.</p>
- *
- * @param cls the Class to check, may be null
- * @param toClass the Class to try to assign into, returns false if null
- * @return {@code true} if assignment possible
- */
- public static boolean isAssignable(Class<?> cls, Class<?> toClass) {
- return isAssignable(cls, toClass, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5));
- }
-
- /**
- * <p>Checks if one {@code Class} can be assigned to a variable of
- * another {@code Class}.</p>
- *
- * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method,
- * this method takes into account widenings of primitive classes and
- * {@code null}s.</p>
- *
- * <p>Primitive widenings allow an int to be assigned to a long, float or
- * double. This method returns the correct result for these cases.</p>
- *
- * <p>{@code Null} may be assigned to any reference type. This method
- * will return {@code true} if {@code null} is passed in and the
- * toClass is non-primitive.</p>
- *
- * <p>Specifically, this method tests whether the type represented by the
- * specified {@code Class} parameter can be converted to the type
- * represented by this {@code Class} object via an identity conversion
- * widening primitive or widening reference conversion. See
- * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>,
- * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p>
- *
- * @param cls the Class to check, may be null
- * @param toClass the Class to try to assign into, returns false if null
- * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers
- * @return {@code true} if assignment possible
- */
- public static boolean isAssignable(Class<?> cls, Class<?> toClass, boolean autoboxing) {
- if (toClass == null) {
- return false;
- }
- // have to check for null, as isAssignableFrom doesn't
- if (cls == null) {
- return !(toClass.isPrimitive());
- }
- //autoboxing:
- if (autoboxing) {
- if (cls.isPrimitive() && !toClass.isPrimitive()) {
- cls = primitiveToWrapper(cls);
- if (cls == null) {
- return false;
- }
- }
- if (toClass.isPrimitive() && !cls.isPrimitive()) {
- cls = wrapperToPrimitive(cls);
- if (cls == null) {
- return false;
- }
- }
- }
- if (cls.equals(toClass)) {
- return true;
- }
- if (cls.isPrimitive()) {
- if (toClass.isPrimitive() == false) {
- return false;
- }
- if (Integer.TYPE.equals(cls)) {
- return Long.TYPE.equals(toClass)
- || Float.TYPE.equals(toClass)
- || Double.TYPE.equals(toClass);
- }
- if (Long.TYPE.equals(cls)) {
- return Float.TYPE.equals(toClass)
- || Double.TYPE.equals(toClass);
- }
- if (Boolean.TYPE.equals(cls)) {
- return false;
- }
- if (Double.TYPE.equals(cls)) {
- return false;
- }
- if (Float.TYPE.equals(cls)) {
- return Double.TYPE.equals(toClass);
- }
- if (Character.TYPE.equals(cls)) {
- return Integer.TYPE.equals(toClass)
- || Long.TYPE.equals(toClass)
- || Float.TYPE.equals(toClass)
- || Double.TYPE.equals(toClass);
- }
- if (Short.TYPE.equals(cls)) {
- return Integer.TYPE.equals(toClass)
- || Long.TYPE.equals(toClass)
- || Float.TYPE.equals(toClass)
- || Double.TYPE.equals(toClass);
- }
- if (Byte.TYPE.equals(cls)) {
- return Short.TYPE.equals(toClass)
- || Integer.TYPE.equals(toClass)
- || Long.TYPE.equals(toClass)
- || Float.TYPE.equals(toClass)
- || Double.TYPE.equals(toClass);
- }
- // should never get here
- return false;
- }
- return toClass.isAssignableFrom(cls);
- }
-
- /**
- * <p>Converts the specified primitive Class object to its corresponding
- * wrapper Class object.</p>
- *
- * <p>NOTE: From v2.2, this method handles {@code Void.TYPE},
- * returning {@code Void.TYPE}.</p>
- *
- * @param cls the class to convert, may be null
- * @return the wrapper class for {@code cls} or {@code cls} if
- * {@code cls} is not a primitive. {@code null} if null input.
- * @since 2.1
- */
- public static Class<?> primitiveToWrapper(Class<?> cls) {
- Class<?> convertedClass = cls;
- if (cls != null && cls.isPrimitive()) {
- convertedClass = primitiveWrapperMap.get(cls);
- }
- return convertedClass;
- }
-
- /**
- * <p>Converts the specified array of primitive Class objects to an array of
- * its corresponding wrapper Class objects.</p>
- *
- * @param classes the class array to convert, may be null or empty
- * @return an array which contains for each given class, the wrapper class or
- * the original class if class is not a primitive. {@code null} if null input.
- * Empty array if an empty array passed in.
- * @since 2.1
- */
- public static Class<?>[] primitivesToWrappers(Class<?>... classes) {
- if (classes == null) {
- return null;
- }
-
- if (classes.length == 0) {
- return classes;
- }
-
- Class<?>[] convertedClasses = new Class[classes.length];
- for (int i = 0; i < classes.length; i++) {
- convertedClasses[i] = primitiveToWrapper(classes[i]);
- }
- return convertedClasses;
- }
-
- /**
- * <p>Converts the specified wrapper class to its corresponding primitive
- * class.</p>
- *
- * <p>This method is the counter part of {@code primitiveToWrapper()}.
- * If the passed in class is a wrapper class for a primitive type, this
- * primitive type will be returned (e.g. {@code Integer.TYPE} for
- * {@code Integer.class}). For other classes, or if the parameter is
- * <b>null</b>, the return value is <b>null</b>.</p>
- *
- * @param cls the class to convert, may be <b>null</b>
- * @return the corresponding primitive type if {@code cls} is a
- * wrapper class, <b>null</b> otherwise
- * @see #primitiveToWrapper(Class)
- * @since 2.4
- */
- public static Class<?> wrapperToPrimitive(Class<?> cls) {
- return wrapperPrimitiveMap.get(cls);
- }
-
- /**
- * <p>Converts the specified array of wrapper Class objects to an array of
- * its corresponding primitive Class objects.</p>
- *
- * <p>This method invokes {@code wrapperToPrimitive()} for each element
- * of the passed in array.</p>
- *
- * @param classes the class array to convert, may be null or empty
- * @return an array which contains for each given class, the primitive class or
- * <b>null</b> if the original class is not a wrapper class. {@code null} if null input.
- * Empty array if an empty array passed in.
- * @see #wrapperToPrimitive(Class)
- * @since 2.4
- */
- public static Class<?>[] wrappersToPrimitives(Class<?>... classes) {
- if (classes == null) {
- return null;
- }
-
- if (classes.length == 0) {
- return classes;
- }
-
- Class<?>[] convertedClasses = new Class[classes.length];
- for (int i = 0; i < classes.length; i++) {
- convertedClasses[i] = wrapperToPrimitive(classes[i]);
- }
- return convertedClasses;
- }
-
- // Inner class
- // ----------------------------------------------------------------------
- /**
- * <p>Is the specified class an inner class or static nested class.</p>
- *
- * @param cls the class to check, may be null
- * @return {@code true} if the class is an inner or static nested class,
- * false if not or {@code null}
- */
- public static boolean isInnerClass(Class<?> cls) {
- return cls != null && cls.getEnclosingClass() != null;
- }
-
- // Class loading
- // ----------------------------------------------------------------------
- /**
- * Returns the class represented by {@code className} using the
- * {@code classLoader}. This implementation supports the syntaxes
- * "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
- * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
- *
- * @param classLoader the class loader to use to load the class
- * @param className the class name
- * @param initialize whether the class must be initialized
- * @return the class represented by {@code className} using the {@code classLoader}
- * @throws ClassNotFoundException if the class is not found
- */
- public static Class<?> getClass(
- ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException {
- try {
- Class<?> clazz;
- if (abbreviationMap.containsKey(className)) {
- String clsName = "[" + abbreviationMap.get(className);
- clazz = Class.forName(clsName, initialize, classLoader).getComponentType();
- } else {
- clazz = Class.forName(toCanonicalName(className), initialize, classLoader);
- }
- return clazz;
- } catch (ClassNotFoundException ex) {
- // allow path separators (.) as inner class name separators
- int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR);
-
- if (lastDotIndex != -1) {
- try {
- return getClass(classLoader, className.substring(0, lastDotIndex) +
- INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1),
- initialize);
- } catch (ClassNotFoundException ex2) { // NOPMD
- // ignore exception
- }
- }
-
- throw ex;
- }
- }
-
- /**
- * Returns the (initialized) class represented by {@code className}
- * using the {@code classLoader}. This implementation supports
- * the syntaxes "{@code java.util.Map.Entry[]}",
- * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
- * and "{@code [Ljava.util.Map$Entry;}".
- *
- * @param classLoader the class loader to use to load the class
- * @param className the class name
- * @return the class represented by {@code className} using the {@code classLoader}
- * @throws ClassNotFoundException if the class is not found
- */
- public static Class<?> getClass(ClassLoader classLoader, String className) throws ClassNotFoundException {
- return getClass(classLoader, className, true);
- }
-
- /**
- * Returns the (initialized) class represented by {@code className}
- * using the current thread's context class loader. This implementation
- * supports the syntaxes "{@code java.util.Map.Entry[]}",
- * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}",
- * and "{@code [Ljava.util.Map$Entry;}".
- *
- * @param className the class name
- * @return the class represented by {@code className} using the current thread's context class loader
- * @throws ClassNotFoundException if the class is not found
- */
- public static Class<?> getClass(String className) throws ClassNotFoundException {
- return getClass(className, true);
- }
-
- /**
- * Returns the class represented by {@code className} using the
- * current thread's context class loader. This implementation supports the
- * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}",
- * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}".
- *
- * @param className the class name
- * @param initialize whether the class must be initialized
- * @return the class represented by {@code className} using the current thread's context class loader
- * @throws ClassNotFoundException if the class is not found
- */
- public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException {
- ClassLoader contextCL = Thread.currentThread().getContextClassLoader();
- ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL;
- return getClass(loader, className, initialize );
- }
-
- // Public method
- // ----------------------------------------------------------------------
- /**
- * <p>Returns the desired Method much like {@code Class.getMethod}, however
- * it ensures that the returned Method is from a public class or interface and not
- * from an anonymous inner class. This means that the Method is invokable and
- * doesn't fall foul of Java bug
- * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>).
- *
- * <code><pre>Set set = Collections.unmodifiableSet(...);
- * Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]);
- * Object result = method.invoke(set, new Object[]);</pre></code>
- * </p>
- *
- * @param cls the class to check, not null
- * @param methodName the name of the method
- * @param parameterTypes the list of parameters
- * @return the method
- * @throws NullPointerException if the class is null
- * @throws SecurityException if a a security violation occured
- * @throws NoSuchMethodException if the method is not found in the given class
- * or if the metothod doen't conform with the requirements
- */
- public static Method getPublicMethod(Class<?> cls, String methodName, Class<?>... parameterTypes)
- throws SecurityException, NoSuchMethodException {
-
- Method declaredMethod = cls.getMethod(methodName, parameterTypes);
- if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) {
- return declaredMethod;
- }
-
- List<Class<?>> candidateClasses = new ArrayList<Class<?>>();
- candidateClasses.addAll(getAllInterfaces(cls));
- candidateClasses.addAll(getAllSuperclasses(cls));
-
- for (Class<?> candidateClass : candidateClasses) {
- if (!Modifier.isPublic(candidateClass.getModifiers())) {
- continue;
- }
- Method candidateMethod;
- try {
- candidateMethod = candidateClass.getMethod(methodName, parameterTypes);
- } catch (NoSuchMethodException ex) {
- continue;
- }
- if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) {
- return candidateMethod;
- }
- }
-
- throw new NoSuchMethodException("Can't find a public method for " +
- methodName + " " + ArrayUtils.toString(parameterTypes));
- }
-
- // ----------------------------------------------------------------------
- /**
- * Converts a class name to a JLS style class name.
- *
- * @param className the class name
- * @return the converted name
- */
- private static String toCanonicalName(String className) {
- className = StringUtils.deleteWhitespace(className);
- if (className == null) {
- throw new NullPointerException("className must not be null.");
- } else if (className.endsWith("[]")) {
- StringBuilder classNameBuffer = new StringBuilder();
- while (className.endsWith("[]")) {
- className = className.substring(0, className.length() - 2);
- classNameBuffer.append("[");
- }
- String abbreviation = abbreviationMap.get(className);
- if (abbreviation != null) {
- classNameBuffer.append(abbreviation);
- } else {
- classNameBuffer.append("L").append(className).append(";");
- }
- className = classNameBuffer.toString();
- }
- return className;
- }
-
- /**
- * <p>Converts an array of {@code Object} in to an array of {@code Class} objects.
- * If any of these objects is null, a null element will be inserted into the array.</p>
- *
- * <p>This method returns {@code null} for a {@code null} input array.</p>
- *
- * @param array an {@code Object} array
- * @return a {@code Class} array, {@code null} if null array input
- * @since 2.4
- */
- public static Class<?>[] toClass(Object... array) {
- if (array == null) {
- return null;
- } else if (array.length == 0) {
- return ArrayUtils.EMPTY_CLASS_ARRAY;
- }
- Class<?>[] classes = new Class[array.length];
- for (int i = 0; i < array.length; i++) {
- classes[i] = array[i] == null ? null : array[i].getClass();
- }
- return classes;
- }
-
- // Short canonical name
- // ----------------------------------------------------------------------
- /**
- * <p>Gets the canonical name minus the package name for an {@code Object}.</p>
- *
- * @param object the class to get the short name for, may be null
- * @param valueIfNull the value to return if null
- * @return the canonical name of the object without the package name, or the null value
- * @since 2.4
- */
- public static String getShortCanonicalName(Object object, String valueIfNull) {
- if (object == null) {
- return valueIfNull;
- }
- return getShortCanonicalName(object.getClass().getName());
- }
-
- /**
- * <p>Gets the canonical name minus the package name from a {@code Class}.</p>
- *
- * @param cls the class to get the short name for.
- * @return the canonical name without the package name or an empty string
- * @since 2.4
- */
- public static String getShortCanonicalName(Class<?> cls) {
- if (cls == null) {
- return StringUtils.EMPTY;
- }
- return getShortCanonicalName(cls.getName());
- }
-
- /**
- * <p>Gets the canonical name minus the package name from a String.</p>
- *
- * <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
- *
- * @param canonicalName the class name to get the short name for
- * @return the canonical name of the class without the package name or an empty string
- * @since 2.4
- */
- public static String getShortCanonicalName(String canonicalName) {
- return ClassUtils.getShortClassName(getCanonicalName(canonicalName));
- }
-
- // Package name
- // ----------------------------------------------------------------------
- /**
- * <p>Gets the package name from the canonical name of an {@code Object}.</p>
- *
- * @param object the class to get the package name for, may be null
- * @param valueIfNull the value to return if null
- * @return the package name of the object, or the null value
- * @since 2.4
- */
- public static String getPackageCanonicalName(Object object, String valueIfNull) {
- if (object == null) {
- return valueIfNull;
- }
- return getPackageCanonicalName(object.getClass().getName());
- }
-
- /**
- * <p>Gets the package name from the canonical name of a {@code Class}.</p>
- *
- * @param cls the class to get the package name for, may be {@code null}.
- * @return the package name or an empty string
- * @since 2.4
- */
- public static String getPackageCanonicalName(Class<?> cls) {
- if (cls == null) {
- return StringUtils.EMPTY;
- }
- return getPackageCanonicalName(cls.getName());
- }
-
- /**
- * <p>Gets the package name from the canonical name. </p>
- *
- * <p>The string passed in is assumed to be a canonical name - it is not checked.</p>
- * <p>If the class is unpackaged, return an empty string.</p>
- *
- * @param canonicalName the canonical name to get the package name for, may be {@code null}
- * @return the package name or an empty string
- * @since 2.4
- */
- public static String getPackageCanonicalName(String canonicalName) {
- return ClassUtils.getPackageName(getCanonicalName(canonicalName));
- }
-
- /**
- * <p>Converts a given name of class into canonical format.
- * If name of class is not a name of array class it returns
- * unchanged name.</p>
- * <p>Example:
- * <ul>
- * <li>{@code getCanonicalName("[I") = "int[]"}</li>
- * <li>{@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}</li>
- * <li>{@code getCanonicalName("java.lang.String") = "java.lang.String"}</li>
- * </ul>
- * </p>
- *
- * @param className the name of class
- * @return canonical form of class name
- * @since 2.4
- */
- private static String getCanonicalName(String className) {
- className = StringUtils.deleteWhitespace(className);
- if (className == null) {
- return null;
- } else {
- int dim = 0;
- while (className.startsWith("[")) {
- dim++;
- className = className.substring(1);
- }
- if (dim < 1) {
- return className;
- } else {
- if (className.startsWith("L")) {
- className = className.substring(
- 1,
- className.endsWith(";")
- ? className.length() - 1
- : className.length());
- } else {
- if (className.length() > 0) {
- className = reverseAbbreviationMap.get(className.substring(0, 1));
- }
- }
- StringBuilder canonicalClassNameBuffer = new StringBuilder(className);
- for (int i = 0; i < dim; i++) {
- canonicalClassNameBuffer.append("[]");
- }
- return canonicalClassNameBuffer.toString();
- }
- }
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + + +/** + * <p>Operates on classes without using reflection.</p> + * + * <p>This class handles invalid {@code null} inputs as best it can. + * Each method documents its behaviour in more detail.</p> + * + * <p>The notion of a {@code canonical name} includes the human + * readable name for the type, for example {@code int[]}. The + * non-canonical method variants work with the JVM names, such as + * {@code [I}. </p> + * + * @since 2.0 + * @version $Id: ClassUtils.java 1145035 2011-07-11 06:09:39Z bayard $ + */ +public class ClassUtils { + + /** + * <p>The package separator character: <code>'.' == {@value}</code>.</p> + */ + public static final char PACKAGE_SEPARATOR_CHAR = '.'; + + /** + * <p>The package separator String: {@code "."}.</p> + */ + public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); + + /** + * <p>The inner class separator character: <code>'$' == {@value}</code>.</p> + */ + public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; + + /** + * <p>The inner class separator String: {@code "$"}.</p> + */ + public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); + + /** + * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. + */ + private static final Map<Class<?>, Class<?>> primitiveWrapperMap = new HashMap<Class<?>, Class<?>>(); + static { + primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); + primitiveWrapperMap.put(Byte.TYPE, Byte.class); + primitiveWrapperMap.put(Character.TYPE, Character.class); + primitiveWrapperMap.put(Short.TYPE, Short.class); + primitiveWrapperMap.put(Integer.TYPE, Integer.class); + primitiveWrapperMap.put(Long.TYPE, Long.class); + primitiveWrapperMap.put(Double.TYPE, Double.class); + primitiveWrapperMap.put(Float.TYPE, Float.class); + primitiveWrapperMap.put(Void.TYPE, Void.TYPE); + } + + /** + * Maps wrapper {@code Class}es to their corresponding primitive types. + */ + private static final Map<Class<?>, Class<?>> wrapperPrimitiveMap = new HashMap<Class<?>, Class<?>>(); + static { + for (Class<?> primitiveClass : primitiveWrapperMap.keySet()) { + Class<?> wrapperClass = primitiveWrapperMap.get(primitiveClass); + if (!primitiveClass.equals(wrapperClass)) { + wrapperPrimitiveMap.put(wrapperClass, primitiveClass); + } + } + } + + /** + * Maps a primitive class name to its corresponding abbreviation used in array class names. + */ + private static final Map<String, String> abbreviationMap = new HashMap<String, String>(); + + /** + * Maps an abbreviation used in array class names to corresponding primitive class name. + */ + private static final Map<String, String> reverseAbbreviationMap = new HashMap<String, String>(); + + /** + * Add primitive type abbreviation to maps of abbreviations. + * + * @param primitive Canonical name of primitive type + * @param abbreviation Corresponding abbreviation of primitive type + */ + private static void addAbbreviation(String primitive, String abbreviation) { + abbreviationMap.put(primitive, abbreviation); + reverseAbbreviationMap.put(abbreviation, primitive); + } + + /** + * Feed abbreviation maps + */ + static { + addAbbreviation("int", "I"); + addAbbreviation("boolean", "Z"); + addAbbreviation("float", "F"); + addAbbreviation("long", "J"); + addAbbreviation("short", "S"); + addAbbreviation("byte", "B"); + addAbbreviation("double", "D"); + addAbbreviation("char", "C"); + } + + /** + * <p>ClassUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as + * {@code ClassUtils.getShortClassName(cls)}.</p> + * + * <p>This constructor is public to permit tools that require a JavaBean + * instance to operate.</p> + */ + public ClassUtils() { + super(); + } + + // Short class name + // ---------------------------------------------------------------------- + /** + * <p>Gets the class name minus the package name for an {@code Object}.</p> + * + * @param object the class to get the short name for, may be null + * @param valueIfNull the value to return if null + * @return the class name of the object without the package name, or the null value + */ + public static String getShortClassName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortClassName(object.getClass()); + } + + /** + * <p>Gets the class name minus the package name from a {@code Class}.</p> + * + * <p>Consider using the Java 5 API {@link Class#getSimpleName()} instead. + * The one known difference is that this code will return {@code "Map.Entry"} while + * the {@code java.lang.Class} variant will simply return {@code "Entry"}. </p> + * + * @param cls the class to get the short name for. + * @return the class name without the package name or an empty string + */ + public static String getShortClassName(Class<?> cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getShortClassName(cls.getName()); + } + + /** + * <p>Gets the class name minus the package name from a String.</p> + * + * <p>The string passed in is assumed to be a class name - it is not checked.</p> + + * <p>Note that this method differs from Class.getSimpleName() in that this will + * return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply + * return {@code "Entry"}. </p> + * + * @param className the className to get the short name for + * @return the class name of the class without the package name or an empty string + */ + public static String getShortClassName(String className) { + if (className == null) { + return StringUtils.EMPTY; + } + if (className.length() == 0) { + return StringUtils.EMPTY; + } + + StringBuilder arrayPrefix = new StringBuilder(); + + // Handle array encoding + if (className.startsWith("[")) { + while (className.charAt(0) == '[') { + className = className.substring(1); + arrayPrefix.append("[]"); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1, className.length() - 1); + } + } + + if (reverseAbbreviationMap.containsKey(className)) { + className = reverseAbbreviationMap.get(className); + } + + int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + int innerIdx = className.indexOf( + INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); + String out = className.substring(lastDotIdx + 1); + if (innerIdx != -1) { + out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); + } + return out + arrayPrefix; + } + + /** + * <p>Null-safe version of <code>aClass.getSimpleName()</code></p> + * + * @param cls the class for which to get the simple name. + * @return the simple class name. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(Class<?> cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return cls.getSimpleName(); + } + + /** + * <p>Null-safe version of <code>aClass.getSimpleName()</code></p> + * + * @param object the object for which to get the simple class name. + * @param valueIfNull the value to return if <code>object</code> is <code>null</code> + * @return the simple class name. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getSimpleName(object.getClass()); + } + + // Package name + // ---------------------------------------------------------------------- + /** + * <p>Gets the package name of an {@code Object}.</p> + * + * @param object the class to get the package name for, may be null + * @param valueIfNull the value to return if null + * @return the package name of the object, or the null value + */ + public static String getPackageName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageName(object.getClass()); + } + + /** + * <p>Gets the package name of a {@code Class}.</p> + * + * @param cls the class to get the package name for, may be {@code null}. + * @return the package name or an empty string + */ + public static String getPackageName(Class<?> cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageName(cls.getName()); + } + + /** + * <p>Gets the package name from a {@code String}.</p> + * + * <p>The string passed in is assumed to be a class name - it is not checked.</p> + * <p>If the class is unpackaged, return an empty string.</p> + * + * @param className the className to get the package name for, may be {@code null} + * @return the package name or an empty string + */ + public static String getPackageName(String className) { + if (className == null || className.length() == 0) { + return StringUtils.EMPTY; + } + + // Strip array encoding + while (className.charAt(0) == '[') { + className = className.substring(1); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1); + } + + int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + if (i == -1) { + return StringUtils.EMPTY; + } + return className.substring(0, i); + } + + // Superclasses/Superinterfaces + // ---------------------------------------------------------------------- + /** + * <p>Gets a {@code List} of superclasses for the given class.</p> + * + * @param cls the class to look up, may be {@code null} + * @return the {@code List} of superclasses in order going up from this one + * {@code null} if null input + */ + public static List<Class<?>> getAllSuperclasses(Class<?> cls) { + if (cls == null) { + return null; + } + List<Class<?>> classes = new ArrayList<Class<?>>(); + Class<?> superclass = cls.getSuperclass(); + while (superclass != null) { + classes.add(superclass); + superclass = superclass.getSuperclass(); + } + return classes; + } + + /** + * <p>Gets a {@code List} of all interfaces implemented by the given + * class and its superclasses.</p> + * + * <p>The order is determined by looking through each interface in turn as + * declared in the source file and following its hierarchy up. Then each + * superclass is considered in the same way. Later duplicates are ignored, + * so the order is maintained.</p> + * + * @param cls the class to look up, may be {@code null} + * @return the {@code List} of interfaces in order, + * {@code null} if null input + */ + public static List<Class<?>> getAllInterfaces(Class<?> cls) { + if (cls == null) { + return null; + } + + LinkedHashSet<Class<?>> interfacesFound = new LinkedHashSet<Class<?>>(); + getAllInterfaces(cls, interfacesFound); + + return new ArrayList<Class<?>>(interfacesFound); + } + + /** + * Get the interfaces for the specified class. + * + * @param cls the class to look up, may be {@code null} + * @param interfacesFound the {@code Set} of interfaces for the class + */ + private static void getAllInterfaces(Class<?> cls, HashSet<Class<?>> interfacesFound) { + while (cls != null) { + Class<?>[] interfaces = cls.getInterfaces(); + + for (Class<?> i : interfaces) { + if (interfacesFound.add(i)) { + getAllInterfaces(i, interfacesFound); + } + } + + cls = cls.getSuperclass(); + } + } + + // Convert list + // ---------------------------------------------------------------------- + /** + * <p>Given a {@code List} of class names, this method converts them into classes.</p> + * + * <p>A new {@code List} is returned. If the class name cannot be found, {@code null} + * is stored in the {@code List}. If the class name in the {@code List} is + * {@code null}, {@code null} is stored in the output {@code List}.</p> + * + * @param classNames the classNames to change + * @return a {@code List} of Class objects corresponding to the class names, + * {@code null} if null input + * @throws ClassCastException if classNames contains a non String entry + */ + public static List<Class<?>> convertClassNamesToClasses(List<String> classNames) { + if (classNames == null) { + return null; + } + List<Class<?>> classes = new ArrayList<Class<?>>(classNames.size()); + for (String className : classNames) { + try { + classes.add(Class.forName(className)); + } catch (Exception ex) { + classes.add(null); + } + } + return classes; + } + + /** + * <p>Given a {@code List} of {@code Class} objects, this method converts + * them into class names.</p> + * + * <p>A new {@code List} is returned. {@code null} objects will be copied into + * the returned list as {@code null}.</p> + * + * @param classes the classes to change + * @return a {@code List} of class names corresponding to the Class objects, + * {@code null} if null input + * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry + */ + public static List<String> convertClassesToClassNames(List<Class<?>> classes) { + if (classes == null) { + return null; + } + List<String> classNames = new ArrayList<String>(classes.size()); + for (Class<?> cls : classes) { + if (cls == null) { + classNames.add(null); + } else { + classNames.add(cls.getName()); + } + } + return classNames; + } + + // Is assignable + // ---------------------------------------------------------------------- + /** + * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> + * + * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each + * Class pair in the input arrays. It can be used to check if a set of arguments + * (the first parameter) are suitably compatible with a set of method parameter types + * (the second parameter).</p> + * + * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this + * method takes into account widenings of primitive classes and + * {@code null}s.</p> + * + * <p>Primitive widenings allow an int to be assigned to a {@code long}, + * {@code float} or {@code double}. This method returns the correct + * result for these cases.</p> + * + * <p>{@code Null} may be assigned to any reference type. This method will + * return {@code true} if {@code null} is passed in and the toClass is + * non-primitive.</p> + * + * <p>Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> + * + * <p><strong>Since Lang 3.0,</strong> this method will default behavior for + * calculating assignability between primitive and wrapper types <em>corresponding + * to the running Java version</em>; i.e. autoboxing will be the default + * behavior in VMs running Java versions >= 1.5.</p> + * + * @param classArray the array of Classes to check, may be {@code null} + * @param toClassArray the array of Classes to try to assign into, may be {@code null} + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class<?>[] classArray, Class<?>... toClassArray) { + return isAssignable(classArray, toClassArray, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); + } + + /** + * <p>Checks if an array of Classes can be assigned to another array of Classes.</p> + * + * <p>This method calls {@link #isAssignable(Class, Class) isAssignable} for each + * Class pair in the input arrays. It can be used to check if a set of arguments + * (the first parameter) are suitably compatible with a set of method parameter types + * (the second parameter).</p> + * + * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this + * method takes into account widenings of primitive classes and + * {@code null}s.</p> + * + * <p>Primitive widenings allow an int to be assigned to a {@code long}, + * {@code float} or {@code double}. This method returns the correct + * result for these cases.</p> + * + * <p>{@code Null} may be assigned to any reference type. This method will + * return {@code true} if {@code null} is passed in and the toClass is + * non-primitive.</p> + * + * <p>Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> + * + * @param classArray the array of Classes to check, may be {@code null} + * @param toClassArray the array of Classes to try to assign into, may be {@code null} + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class<?>[] classArray, Class<?>[] toClassArray, boolean autoboxing) { + if (ArrayUtils.isSameLength(classArray, toClassArray) == false) { + return false; + } + if (classArray == null) { + classArray = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (toClassArray == null) { + toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; + } + for (int i = 0; i < classArray.length; i++) { + if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if one {@code Class} can be assigned to a variable of + * another {@code Class}.</p> + * + * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, + * this method takes into account widenings of primitive classes and + * {@code null}s.</p> + * + * <p>Primitive widenings allow an int to be assigned to a long, float or + * double. This method returns the correct result for these cases.</p> + * + * <p>{@code Null} may be assigned to any reference type. This method + * will return {@code true} if {@code null} is passed in and the + * toClass is non-primitive.</p> + * + * <p>Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> + * + * <p><strong>Since Lang 3.0,</strong> this method will default behavior for + * calculating assignability between primitive and wrapper types <em>corresponding + * to the running Java version</em>; i.e. autoboxing will be the default + * behavior in VMs running Java versions >= 1.5.</p> + * + * @param cls the Class to check, may be null + * @param toClass the Class to try to assign into, returns false if null + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class<?> cls, Class<?> toClass) { + return isAssignable(cls, toClass, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); + } + + /** + * <p>Checks if one {@code Class} can be assigned to a variable of + * another {@code Class}.</p> + * + * <p>Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, + * this method takes into account widenings of primitive classes and + * {@code null}s.</p> + * + * <p>Primitive widenings allow an int to be assigned to a long, float or + * double. This method returns the correct result for these cases.</p> + * + * <p>{@code Null} may be assigned to any reference type. This method + * will return {@code true} if {@code null} is passed in and the + * toClass is non-primitive.</p> + * + * <p>Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * <em><a href="http://java.sun.com/docs/books/jls/">The Java Language Specification</a></em>, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.</p> + * + * @param cls the Class to check, may be null + * @param toClass the Class to try to assign into, returns false if null + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class<?> cls, Class<?> toClass, boolean autoboxing) { + if (toClass == null) { + return false; + } + // have to check for null, as isAssignableFrom doesn't + if (cls == null) { + return !(toClass.isPrimitive()); + } + //autoboxing: + if (autoboxing) { + if (cls.isPrimitive() && !toClass.isPrimitive()) { + cls = primitiveToWrapper(cls); + if (cls == null) { + return false; + } + } + if (toClass.isPrimitive() && !cls.isPrimitive()) { + cls = wrapperToPrimitive(cls); + if (cls == null) { + return false; + } + } + } + if (cls.equals(toClass)) { + return true; + } + if (cls.isPrimitive()) { + if (toClass.isPrimitive() == false) { + return false; + } + if (Integer.TYPE.equals(cls)) { + return Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Long.TYPE.equals(cls)) { + return Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Boolean.TYPE.equals(cls)) { + return false; + } + if (Double.TYPE.equals(cls)) { + return false; + } + if (Float.TYPE.equals(cls)) { + return Double.TYPE.equals(toClass); + } + if (Character.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Short.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Byte.TYPE.equals(cls)) { + return Short.TYPE.equals(toClass) + || Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + // should never get here + return false; + } + return toClass.isAssignableFrom(cls); + } + + /** + * <p>Converts the specified primitive Class object to its corresponding + * wrapper Class object.</p> + * + * <p>NOTE: From v2.2, this method handles {@code Void.TYPE}, + * returning {@code Void.TYPE}.</p> + * + * @param cls the class to convert, may be null + * @return the wrapper class for {@code cls} or {@code cls} if + * {@code cls} is not a primitive. {@code null} if null input. + * @since 2.1 + */ + public static Class<?> primitiveToWrapper(Class<?> cls) { + Class<?> convertedClass = cls; + if (cls != null && cls.isPrimitive()) { + convertedClass = primitiveWrapperMap.get(cls); + } + return convertedClass; + } + + /** + * <p>Converts the specified array of primitive Class objects to an array of + * its corresponding wrapper Class objects.</p> + * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the wrapper class or + * the original class if class is not a primitive. {@code null} if null input. + * Empty array if an empty array passed in. + * @since 2.1 + */ + public static Class<?>[] primitivesToWrappers(Class<?>... classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class<?>[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = primitiveToWrapper(classes[i]); + } + return convertedClasses; + } + + /** + * <p>Converts the specified wrapper class to its corresponding primitive + * class.</p> + * + * <p>This method is the counter part of {@code primitiveToWrapper()}. + * If the passed in class is a wrapper class for a primitive type, this + * primitive type will be returned (e.g. {@code Integer.TYPE} for + * {@code Integer.class}). For other classes, or if the parameter is + * <b>null</b>, the return value is <b>null</b>.</p> + * + * @param cls the class to convert, may be <b>null</b> + * @return the corresponding primitive type if {@code cls} is a + * wrapper class, <b>null</b> otherwise + * @see #primitiveToWrapper(Class) + * @since 2.4 + */ + public static Class<?> wrapperToPrimitive(Class<?> cls) { + return wrapperPrimitiveMap.get(cls); + } + + /** + * <p>Converts the specified array of wrapper Class objects to an array of + * its corresponding primitive Class objects.</p> + * + * <p>This method invokes {@code wrapperToPrimitive()} for each element + * of the passed in array.</p> + * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the primitive class or + * <b>null</b> if the original class is not a wrapper class. {@code null} if null input. + * Empty array if an empty array passed in. + * @see #wrapperToPrimitive(Class) + * @since 2.4 + */ + public static Class<?>[] wrappersToPrimitives(Class<?>... classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class<?>[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = wrapperToPrimitive(classes[i]); + } + return convertedClasses; + } + + // Inner class + // ---------------------------------------------------------------------- + /** + * <p>Is the specified class an inner class or static nested class.</p> + * + * @param cls the class to check, may be null + * @return {@code true} if the class is an inner or static nested class, + * false if not or {@code null} + */ + public static boolean isInnerClass(Class<?> cls) { + return cls != null && cls.getEnclosingClass() != null; + } + + // Class loading + // ---------------------------------------------------------------------- + /** + * Returns the class represented by {@code className} using the + * {@code classLoader}. This implementation supports the syntaxes + * "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by {@code className} using the {@code classLoader} + * @throws ClassNotFoundException if the class is not found + */ + public static Class<?> getClass( + ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException { + try { + Class<?> clazz; + if (abbreviationMap.containsKey(className)) { + String clsName = "[" + abbreviationMap.get(className); + clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); + } else { + clazz = Class.forName(toCanonicalName(className), initialize, classLoader); + } + return clazz; + } catch (ClassNotFoundException ex) { + // allow path separators (.) as inner class name separators + int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + + if (lastDotIndex != -1) { + try { + return getClass(classLoader, className.substring(0, lastDotIndex) + + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), + initialize); + } catch (ClassNotFoundException ex2) { // NOPMD + // ignore exception + } + } + + throw ex; + } + } + + /** + * Returns the (initialized) class represented by {@code className} + * using the {@code classLoader}. This implementation supports + * the syntaxes "{@code java.util.Map.Entry[]}", + * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", + * and "{@code [Ljava.util.Map$Entry;}". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @return the class represented by {@code className} using the {@code classLoader} + * @throws ClassNotFoundException if the class is not found + */ + public static Class<?> getClass(ClassLoader classLoader, String className) throws ClassNotFoundException { + return getClass(classLoader, className, true); + } + + /** + * Returns the (initialized) class represented by {@code className} + * using the current thread's context class loader. This implementation + * supports the syntaxes "{@code java.util.Map.Entry[]}", + * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", + * and "{@code [Ljava.util.Map$Entry;}". + * + * @param className the class name + * @return the class represented by {@code className} using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class<?> getClass(String className) throws ClassNotFoundException { + return getClass(className, true); + } + + /** + * Returns the class represented by {@code className} using the + * current thread's context class loader. This implementation supports the + * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by {@code className} using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class<?> getClass(String className, boolean initialize) throws ClassNotFoundException { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; + return getClass(loader, className, initialize ); + } + + // Public method + // ---------------------------------------------------------------------- + /** + * <p>Returns the desired Method much like {@code Class.getMethod}, however + * it ensures that the returned Method is from a public class or interface and not + * from an anonymous inner class. This means that the Method is invokable and + * doesn't fall foul of Java bug + * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957">4071957</a>). + * + * <code><pre>Set set = Collections.unmodifiableSet(...); + * Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]); + * Object result = method.invoke(set, new Object[]);</pre></code> + * </p> + * + * @param cls the class to check, not null + * @param methodName the name of the method + * @param parameterTypes the list of parameters + * @return the method + * @throws NullPointerException if the class is null + * @throws SecurityException if a a security violation occured + * @throws NoSuchMethodException if the method is not found in the given class + * or if the metothod doen't conform with the requirements + */ + public static Method getPublicMethod(Class<?> cls, String methodName, Class<?>... parameterTypes) + throws SecurityException, NoSuchMethodException { + + Method declaredMethod = cls.getMethod(methodName, parameterTypes); + if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { + return declaredMethod; + } + + List<Class<?>> candidateClasses = new ArrayList<Class<?>>(); + candidateClasses.addAll(getAllInterfaces(cls)); + candidateClasses.addAll(getAllSuperclasses(cls)); + + for (Class<?> candidateClass : candidateClasses) { + if (!Modifier.isPublic(candidateClass.getModifiers())) { + continue; + } + Method candidateMethod; + try { + candidateMethod = candidateClass.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException ex) { + continue; + } + if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { + return candidateMethod; + } + } + + throw new NoSuchMethodException("Can't find a public method for " + + methodName + " " + ArrayUtils.toString(parameterTypes)); + } + + // ---------------------------------------------------------------------- + /** + * Converts a class name to a JLS style class name. + * + * @param className the class name + * @return the converted name + */ + private static String toCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + throw new NullPointerException("className must not be null."); + } else if (className.endsWith("[]")) { + StringBuilder classNameBuffer = new StringBuilder(); + while (className.endsWith("[]")) { + className = className.substring(0, className.length() - 2); + classNameBuffer.append("["); + } + String abbreviation = abbreviationMap.get(className); + if (abbreviation != null) { + classNameBuffer.append(abbreviation); + } else { + classNameBuffer.append("L").append(className).append(";"); + } + className = classNameBuffer.toString(); + } + return className; + } + + /** + * <p>Converts an array of {@code Object} in to an array of {@code Class} objects. + * If any of these objects is null, a null element will be inserted into the array.</p> + * + * <p>This method returns {@code null} for a {@code null} input array.</p> + * + * @param array an {@code Object} array + * @return a {@code Class} array, {@code null} if null array input + * @since 2.4 + */ + public static Class<?>[] toClass(Object... array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return ArrayUtils.EMPTY_CLASS_ARRAY; + } + Class<?>[] classes = new Class[array.length]; + for (int i = 0; i < array.length; i++) { + classes[i] = array[i] == null ? null : array[i].getClass(); + } + return classes; + } + + // Short canonical name + // ---------------------------------------------------------------------- + /** + * <p>Gets the canonical name minus the package name for an {@code Object}.</p> + * + * @param object the class to get the short name for, may be null + * @param valueIfNull the value to return if null + * @return the canonical name of the object without the package name, or the null value + * @since 2.4 + */ + public static String getShortCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortCanonicalName(object.getClass().getName()); + } + + /** + * <p>Gets the canonical name minus the package name from a {@code Class}.</p> + * + * @param cls the class to get the short name for. + * @return the canonical name without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(Class<?> cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getShortCanonicalName(cls.getName()); + } + + /** + * <p>Gets the canonical name minus the package name from a String.</p> + * + * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> + * + * @param canonicalName the class name to get the short name for + * @return the canonical name of the class without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(String canonicalName) { + return ClassUtils.getShortClassName(getCanonicalName(canonicalName)); + } + + // Package name + // ---------------------------------------------------------------------- + /** + * <p>Gets the package name from the canonical name of an {@code Object}.</p> + * + * @param object the class to get the package name for, may be null + * @param valueIfNull the value to return if null + * @return the package name of the object, or the null value + * @since 2.4 + */ + public static String getPackageCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageCanonicalName(object.getClass().getName()); + } + + /** + * <p>Gets the package name from the canonical name of a {@code Class}.</p> + * + * @param cls the class to get the package name for, may be {@code null}. + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(Class<?> cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageCanonicalName(cls.getName()); + } + + /** + * <p>Gets the package name from the canonical name. </p> + * + * <p>The string passed in is assumed to be a canonical name - it is not checked.</p> + * <p>If the class is unpackaged, return an empty string.</p> + * + * @param canonicalName the canonical name to get the package name for, may be {@code null} + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(String canonicalName) { + return ClassUtils.getPackageName(getCanonicalName(canonicalName)); + } + + /** + * <p>Converts a given name of class into canonical format. + * If name of class is not a name of array class it returns + * unchanged name.</p> + * <p>Example: + * <ul> + * <li>{@code getCanonicalName("[I") = "int[]"}</li> + * <li>{@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}</li> + * <li>{@code getCanonicalName("java.lang.String") = "java.lang.String"}</li> + * </ul> + * </p> + * + * @param className the name of class + * @return canonical form of class name + * @since 2.4 + */ + private static String getCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + return null; + } else { + int dim = 0; + while (className.startsWith("[")) { + dim++; + className = className.substring(1); + } + if (dim < 1) { + return className; + } else { + if (className.startsWith("L")) { + className = className.substring( + 1, + className.endsWith(";") + ? className.length() - 1 + : className.length()); + } else { + if (className.length() > 0) { + className = reverseAbbreviationMap.get(className.substring(0, 1)); + } + } + StringBuilder canonicalClassNameBuffer = new StringBuilder(className); + for (int i = 0; i < dim; i++) { + canonicalClassNameBuffer.append("[]"); + } + return canonicalClassNameBuffer.toString(); + } + } + } +} diff --git a/src/org/apache/commons/lang3/JavaVersion.java b/src/org/apache/commons/lang3/JavaVersion.java index 4901f6d..a33735c 100644 --- a/src/org/apache/commons/lang3/JavaVersion.java +++ b/src/org/apache/commons/lang3/JavaVersion.java @@ -1,168 +1,168 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-/**
- * <p>An enum representing all the versions of the Java specification.
- * This is intended to mirror available values from the
- * <em>java.specification.version</em> System property. </p>
- *
- * @since 3.0
- * @version $Id: $
- */
-public enum JavaVersion {
-
- /**
- * The Java version reported by Android. This is not an official Java version number.
- */
- JAVA_0_9(1.5f, "0.9"),
-
- /**
- * Java 1.1.
- */
- JAVA_1_1(1.1f, "1.1"),
-
- /**
- * Java 1.2.
- */
- JAVA_1_2(1.2f, "1.2"),
-
- /**
- * Java 1.3.
- */
- JAVA_1_3(1.3f, "1.3"),
-
- /**
- * Java 1.4.
- */
- JAVA_1_4(1.4f, "1.4"),
-
- /**
- * Java 1.5.
- */
- JAVA_1_5(1.5f, "1.5"),
-
- /**
- * Java 1.6.
- */
- JAVA_1_6(1.6f, "1.6"),
-
- /**
- * Java 1.7.
- */
- JAVA_1_7(1.7f, "1.7"),
-
- /**
- * Java 1.8.
- */
- JAVA_1_8(1.8f, "1.8");
-
- /**
- * The float value.
- */
- private float value;
- /**
- * The standard name.
- */
- private String name;
-
- /**
- * Constructor.
- *
- * @param value the float value
- * @param name the standard name, not null
- */
- JavaVersion(final float value, final String name) {
- this.value = value;
- this.name = name;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Whether this version of Java is at least the version of Java passed in.</p>
- *
- * <p>For example:<br />
- * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p>
- *
- * @param requiredVersion the version to check against, not null
- * @return true if this version is equal to or greater than the specified version
- */
- public boolean atLeast(JavaVersion requiredVersion) {
- return this.value >= requiredVersion.value;
- }
-
- /**
- * Transforms the given string with a Java version number to the
- * corresponding constant of this enumeration class. This method is used
- * internally.
- *
- * @param nom the Java version as string
- * @return the corresponding enumeration constant or <b>null</b> if the
- * version is unknown
- */
- // helper for static importing
- static JavaVersion getJavaVersion(final String nom) {
- return get(nom);
- }
-
- /**
- * Transforms the given string with a Java version number to the
- * corresponding constant of this enumeration class. This method is used
- * internally.
- *
- * @param nom the Java version as string
- * @return the corresponding enumeration constant or <b>null</b> if the
- * version is unknown
- */
- static JavaVersion get(final String nom) {
- if ("0.9".equals(nom)) {
- return JAVA_0_9;
- } else if ("1.1".equals(nom)) {
- return JAVA_1_1;
- } else if ("1.2".equals(nom)) {
- return JAVA_1_2;
- } else if ("1.3".equals(nom)) {
- return JAVA_1_3;
- } else if ("1.4".equals(nom)) {
- return JAVA_1_4;
- } else if ("1.5".equals(nom)) {
- return JAVA_1_5;
- } else if ("1.6".equals(nom)) {
- return JAVA_1_6;
- } else if ("1.7".equals(nom)) {
- return JAVA_1_7;
- } else if ("1.8".equals(nom)) {
- return JAVA_1_8;
- } else {
- return null;
- }
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>The string value is overridden to return the standard name.</p>
- *
- * <p>For example, <code>"1.5"</code>.</p>
- *
- * @return the name, not null
- */
- @Override
- public String toString() {
- return name;
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + * <p>An enum representing all the versions of the Java specification. + * This is intended to mirror available values from the + * <em>java.specification.version</em> System property. </p> + * + * @since 3.0 + * @version $Id: $ + */ +public enum JavaVersion { + + /** + * The Java version reported by Android. This is not an official Java version number. + */ + JAVA_0_9(1.5f, "0.9"), + + /** + * Java 1.1. + */ + JAVA_1_1(1.1f, "1.1"), + + /** + * Java 1.2. + */ + JAVA_1_2(1.2f, "1.2"), + + /** + * Java 1.3. + */ + JAVA_1_3(1.3f, "1.3"), + + /** + * Java 1.4. + */ + JAVA_1_4(1.4f, "1.4"), + + /** + * Java 1.5. + */ + JAVA_1_5(1.5f, "1.5"), + + /** + * Java 1.6. + */ + JAVA_1_6(1.6f, "1.6"), + + /** + * Java 1.7. + */ + JAVA_1_7(1.7f, "1.7"), + + /** + * Java 1.8. + */ + JAVA_1_8(1.8f, "1.8"); + + /** + * The float value. + */ + private float value; + /** + * The standard name. + */ + private String name; + + /** + * Constructor. + * + * @param value the float value + * @param name the standard name, not null + */ + JavaVersion(final float value, final String name) { + this.value = value; + this.name = name; + } + + //----------------------------------------------------------------------- + /** + * <p>Whether this version of Java is at least the version of Java passed in.</p> + * + * <p>For example:<br /> + * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}<p> + * + * @param requiredVersion the version to check against, not null + * @return true if this version is equal to or greater than the specified version + */ + public boolean atLeast(JavaVersion requiredVersion) { + return this.value >= requiredVersion.value; + } + + /** + * Transforms the given string with a Java version number to the + * corresponding constant of this enumeration class. This method is used + * internally. + * + * @param nom the Java version as string + * @return the corresponding enumeration constant or <b>null</b> if the + * version is unknown + */ + // helper for static importing + static JavaVersion getJavaVersion(final String nom) { + return get(nom); + } + + /** + * Transforms the given string with a Java version number to the + * corresponding constant of this enumeration class. This method is used + * internally. + * + * @param nom the Java version as string + * @return the corresponding enumeration constant or <b>null</b> if the + * version is unknown + */ + static JavaVersion get(final String nom) { + if ("0.9".equals(nom)) { + return JAVA_0_9; + } else if ("1.1".equals(nom)) { + return JAVA_1_1; + } else if ("1.2".equals(nom)) { + return JAVA_1_2; + } else if ("1.3".equals(nom)) { + return JAVA_1_3; + } else if ("1.4".equals(nom)) { + return JAVA_1_4; + } else if ("1.5".equals(nom)) { + return JAVA_1_5; + } else if ("1.6".equals(nom)) { + return JAVA_1_6; + } else if ("1.7".equals(nom)) { + return JAVA_1_7; + } else if ("1.8".equals(nom)) { + return JAVA_1_8; + } else { + return null; + } + } + + //----------------------------------------------------------------------- + /** + * <p>The string value is overridden to return the standard name.</p> + * + * <p>For example, <code>"1.5"</code>.</p> + * + * @return the name, not null + */ + @Override + public String toString() { + return name; + } + +} diff --git a/src/org/apache/commons/lang3/ObjectUtils.java b/src/org/apache/commons/lang3/ObjectUtils.java index 69dde6f..a75184c 100644 --- a/src/org/apache/commons/lang3/ObjectUtils.java +++ b/src/org/apache/commons/lang3/ObjectUtils.java @@ -1,608 +1,608 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.TreeSet;
-
-import org.apache.commons.lang3.exception.CloneFailedException;
-import org.apache.commons.lang3.mutable.MutableInt;
-
-/**
- * <p>Operations on {@code Object}.</p>
- *
- * <p>This class tries to handle {@code null} input gracefully.
- * An exception will generally not be thrown for a {@code null} input.
- * Each method documents its behaviour in more detail.</p>
- *
- * <p>#ThreadSafe#</p>
- * @since 1.0
- * @version $Id: ObjectUtils.java 1153350 2011-08-03 05:29:21Z bayard $
- */
-//@Immutable
-public class ObjectUtils {
-
- /**
- * <p>Singleton used as a {@code null} placeholder where
- * {@code null} has another meaning.</p>
- *
- * <p>For example, in a {@code HashMap} the
- * {@link java.util.HashMap#get(java.lang.Object)} method returns
- * {@code null} if the {@code Map} contains {@code null} or if there
- * is no matching key. The {@code Null} placeholder can be used to
- * distinguish between these two cases.</p>
- *
- * <p>Another example is {@code Hashtable}, where {@code null}
- * cannot be stored.</p>
- *
- * <p>This instance is Serializable.</p>
- */
- public static final Null NULL = new Null();
-
- /**
- * <p>{@code ObjectUtils} instances should NOT be constructed in
- * standard programming. Instead, the static methods on the class should
- * be used, such as {@code ObjectUtils.defaultIfNull("a","b");}.</p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean
- * instance to operate.</p>
- */
- public ObjectUtils() {
- super();
- }
-
- // Defaulting
- //-----------------------------------------------------------------------
- /**
- * <p>Returns a default value if the object passed is {@code null}.</p>
- *
- * <pre>
- * ObjectUtils.defaultIfNull(null, null) = null
- * ObjectUtils.defaultIfNull(null, "") = ""
- * ObjectUtils.defaultIfNull(null, "zz") = "zz"
- * ObjectUtils.defaultIfNull("abc", *) = "abc"
- * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
- * </pre>
- *
- * @param <T> the type of the object
- * @param object the {@code Object} to test, may be {@code null}
- * @param defaultValue the default value to return, may be {@code null}
- * @return {@code object} if it is not {@code null}, defaultValue otherwise
- */
- public static <T> T defaultIfNull(T object, T defaultValue) {
- return object != null ? object : defaultValue;
- }
-
- /**
- * <p>Returns the first value in the array which is not {@code null}.
- * If all the values are {@code null} or the array is {@code null}
- * or empty then {@code null} is returned.</p>
- *
- * <pre>
- * ObjectUtils.firstNonNull(null, null) = null
- * ObjectUtils.firstNonNull(null, "") = ""
- * ObjectUtils.firstNonNull(null, null, "") = ""
- * ObjectUtils.firstNonNull(null, "zz") = "zz"
- * ObjectUtils.firstNonNull("abc", *) = "abc"
- * ObjectUtils.firstNonNull(null, "xyz", *) = "xyz"
- * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
- * ObjectUtils.firstNonNull() = null
- * </pre>
- *
- * @param <T> the component type of the array
- * @param values the values to test, may be {@code null} or empty
- * @return the first value from {@code values} which is not {@code null},
- * or {@code null} if there are no non-null values
- * @since 3.0
- */
- public static <T> T firstNonNull(T... values) {
- if (values != null) {
- for (T val : values) {
- if (val != null) {
- return val;
- }
- }
- }
- return null;
- }
-
- // Null-safe equals/hashCode
- //-----------------------------------------------------------------------
- /**
- * <p>Compares two objects for equality, where either one or both
- * objects may be {@code null}.</p>
- *
- * <pre>
- * ObjectUtils.equals(null, null) = true
- * ObjectUtils.equals(null, "") = false
- * ObjectUtils.equals("", null) = false
- * ObjectUtils.equals("", "") = true
- * ObjectUtils.equals(Boolean.TRUE, null) = false
- * ObjectUtils.equals(Boolean.TRUE, "true") = false
- * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true
- * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
- * </pre>
- *
- * @param object1 the first object, may be {@code null}
- * @param object2 the second object, may be {@code null}
- * @return {@code true} if the values of both objects are the same
- */
- public static boolean equals(Object object1, Object object2) {
- if (object1 == object2) {
- return true;
- }
- if ((object1 == null) || (object2 == null)) {
- return false;
- }
- return object1.equals(object2);
- }
-
- /**
- * <p>Compares two objects for inequality, where either one or both
- * objects may be {@code null}.</p>
- *
- * <pre>
- * ObjectUtils.notEqual(null, null) = false
- * ObjectUtils.notEqual(null, "") = true
- * ObjectUtils.notEqual("", null) = true
- * ObjectUtils.notEqual("", "") = false
- * ObjectUtils.notEqual(Boolean.TRUE, null) = true
- * ObjectUtils.notEqual(Boolean.TRUE, "true") = true
- * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE) = false
- * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
- * </pre>
- *
- * @param object1 the first object, may be {@code null}
- * @param object2 the second object, may be {@code null}
- * @return {@code false} if the values of both objects are the same
- */
- public static boolean notEqual(Object object1, Object object2) {
- return ObjectUtils.equals(object1, object2) == false;
- }
-
- /**
- * <p>Gets the hash code of an object returning zero when the
- * object is {@code null}.</p>
- *
- * <pre>
- * ObjectUtils.hashCode(null) = 0
- * ObjectUtils.hashCode(obj) = obj.hashCode()
- * </pre>
- *
- * @param obj the object to obtain the hash code of, may be {@code null}
- * @return the hash code of the object, or zero if null
- * @since 2.1
- */
- public static int hashCode(Object obj) {
- // hashCode(Object) retained for performance, as hash code is often critical
- return (obj == null) ? 0 : obj.hashCode();
- }
-
- /**
- * <p>Gets the hash code for multiple objects.</p>
- *
- * <p>This allows a hash code to be rapidly calculated for a number of objects.
- * The hash code for a single object is the <em>not</em> same as {@link #hashCode(Object)}.
- * The hash code for multiple objects is the same as that calculated by an
- * {@code ArrayList} containing the specified objects.</p>
- *
- * <pre>
- * ObjectUtils.hashCodeMulti() = 1
- * ObjectUtils.hashCodeMulti((Object[]) null) = 1
- * ObjectUtils.hashCodeMulti(a) = 31 + a.hashCode()
- * ObjectUtils.hashCodeMulti(a,b) = (31 + a.hashCode()) * 31 + b.hashCode()
- * ObjectUtils.hashCodeMulti(a,b,c) = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
- * </pre>
- *
- * @param objects the objects to obtain the hash code of, may be {@code null}
- * @return the hash code of the objects, or zero if null
- * @since 3.0
- */
- public static int hashCodeMulti(Object... objects) {
- int hash = 1;
- if (objects != null) {
- for (Object object : objects) {
- hash = hash * 31 + ObjectUtils.hashCode(object);
- }
- }
- return hash;
- }
-
- // Identity ToString
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the toString that would be produced by {@code Object}
- * if a class did not override toString itself. {@code null}
- * will return {@code null}.</p>
- *
- * <pre>
- * ObjectUtils.identityToString(null) = null
- * ObjectUtils.identityToString("") = "java.lang.String@1e23"
- * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
- * </pre>
- *
- * @param object the object to create a toString for, may be
- * {@code null}
- * @return the default toString text, or {@code null} if
- * {@code null} passed in
- */
- public static String identityToString(Object object) {
- if (object == null) {
- return null;
- }
- StringBuffer buffer = new StringBuffer();
- identityToString(buffer, object);
- return buffer.toString();
- }
-
- /**
- * <p>Appends the toString that would be produced by {@code Object}
- * if a class did not override toString itself. {@code null}
- * will throw a NullPointerException for either of the two parameters. </p>
- *
- * <pre>
- * ObjectUtils.identityToString(buf, "") = buf.append("java.lang.String@1e23"
- * ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa"
- * ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa")
- * </pre>
- *
- * @param buffer the buffer to append to
- * @param object the object to create a toString for
- * @since 2.4
- */
- public static void identityToString(StringBuffer buffer, Object object) {
- if (object == null) {
- throw new NullPointerException("Cannot get the toString of a null identity");
- }
- buffer.append(object.getClass().getName())
- .append('@')
- .append(Integer.toHexString(System.identityHashCode(object)));
- }
-
- // ToString
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the {@code toString} of an {@code Object} returning
- * an empty string ("") if {@code null} input.</p>
- *
- * <pre>
- * ObjectUtils.toString(null) = ""
- * ObjectUtils.toString("") = ""
- * ObjectUtils.toString("bat") = "bat"
- * ObjectUtils.toString(Boolean.TRUE) = "true"
- * </pre>
- *
- * @see StringUtils#defaultString(String)
- * @see String#valueOf(Object)
- * @param obj the Object to {@code toString}, may be null
- * @return the passed in Object's toString, or nullStr if {@code null} input
- * @since 2.0
- */
- public static String toString(Object obj) {
- return obj == null ? "" : obj.toString();
- }
-
- /**
- * <p>Gets the {@code toString} of an {@code Object} returning
- * a specified text if {@code null} input.</p>
- *
- * <pre>
- * ObjectUtils.toString(null, null) = null
- * ObjectUtils.toString(null, "null") = "null"
- * ObjectUtils.toString("", "null") = ""
- * ObjectUtils.toString("bat", "null") = "bat"
- * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
- * </pre>
- *
- * @see StringUtils#defaultString(String,String)
- * @see String#valueOf(Object)
- * @param obj the Object to {@code toString}, may be null
- * @param nullStr the String to return if {@code null} input, may be null
- * @return the passed in Object's toString, or nullStr if {@code null} input
- * @since 2.0
- */
- public static String toString(Object obj, String nullStr) {
- return obj == null ? nullStr : obj.toString();
- }
-
- // Comparable
- //-----------------------------------------------------------------------
- /**
- * <p>Null safe comparison of Comparables.</p>
- *
- * @param <T> type of the values processed by this method
- * @param values the set of comparable values, may be null
- * @return
- * <ul>
- * <li>If any objects are non-null and unequal, the lesser object.
- * <li>If all objects are non-null and equal, the first.
- * <li>If any of the comparables are null, the lesser of the non-null objects.
- * <li>If all the comparables are null, null is returned.
- * </ul>
- */
- public static <T extends Comparable<? super T>> T min(T... values) {
- T result = null;
- if (values != null) {
- for (T value : values) {
- if (compare(value, result, true) < 0) {
- result = value;
- }
- }
- }
- return result;
- }
-
- /**
- * <p>Null safe comparison of Comparables.</p>
- *
- * @param <T> type of the values processed by this method
- * @param values the set of comparable values, may be null
- * @return
- * <ul>
- * <li>If any objects are non-null and unequal, the greater object.
- * <li>If all objects are non-null and equal, the first.
- * <li>If any of the comparables are null, the greater of the non-null objects.
- * <li>If all the comparables are null, null is returned.
- * </ul>
- */
- public static <T extends Comparable<? super T>> T max(T... values) {
- T result = null;
- if (values != null) {
- for (T value : values) {
- if (compare(value, result, false) > 0) {
- result = value;
- }
- }
- }
- return result;
- }
-
- /**
- * <p>Null safe comparison of Comparables.
- * {@code null} is assumed to be less than a non-{@code null} value.</p>
- *
- * @param <T> type of the values processed by this method
- * @param c1 the first comparable, may be null
- * @param c2 the second comparable, may be null
- * @return a negative value if c1 < c2, zero if c1 = c2
- * and a positive value if c1 > c2
- */
- public static <T extends Comparable<? super T>> int compare(T c1, T c2) {
- return compare(c1, c2, false);
- }
-
- /**
- * <p>Null safe comparison of Comparables.</p>
- *
- * @param <T> type of the values processed by this method
- * @param c1 the first comparable, may be null
- * @param c2 the second comparable, may be null
- * @param nullGreater if true {@code null} is considered greater
- * than a non-{@code null} value or if false {@code null} is
- * considered less than a Non-{@code null} value
- * @return a negative value if c1 < c2, zero if c1 = c2
- * and a positive value if c1 > c2
- * @see java.util.Comparator#compare(Object, Object)
- */
- public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) {
- if (c1 == c2) {
- return 0;
- } else if (c1 == null) {
- return (nullGreater ? 1 : -1);
- } else if (c2 == null) {
- return (nullGreater ? -1 : 1);
- }
- return c1.compareTo(c2);
- }
-
- /**
- * Find the "best guess" middle value among comparables. If there is an even
- * number of total values, the lower of the two middle values will be returned.
- * @param <T> type of values processed by this method
- * @param items to compare
- * @return T at middle position
- * @throws NullPointerException if items is {@code null}
- * @throws IllegalArgumentException if items is empty or contains {@code null} values
- * @since 3.0.1
- */
- public static <T extends Comparable<? super T>> T median(T... items) {
- Validate.notEmpty(items);
- Validate.noNullElements(items);
- TreeSet<T> sort = new TreeSet<T>();
- Collections.addAll(sort, items);
- @SuppressWarnings("unchecked") //we know all items added were T instances
- T result = (T) sort.toArray()[(sort.size() - 1) / 2];
- return result;
- }
-
- /**
- * Find the "best guess" middle value among comparables. If there is an even
- * number of total values, the lower of the two middle values will be returned.
- * @param <T> type of values processed by this method
- * @param comparator to use for comparisons
- * @param items to compare
- * @return T at middle position
- * @throws NullPointerException if items or comparator is {@code null}
- * @throws IllegalArgumentException if items is empty or contains {@code null} values
- * @since 3.0.1
- */
- public static <T> T median(Comparator<T> comparator, T... items) {
- Validate.notEmpty(items, "null/empty items");
- Validate.noNullElements(items);
- Validate.notNull(comparator, "null comparator");
- TreeSet<T> sort = new TreeSet<T>(comparator);
- Collections.addAll(sort, items);
- @SuppressWarnings("unchecked") //we know all items added were T instances
- T result = (T) sort.toArray()[(sort.size() - 1) / 2];
- return result;
- }
-
- // Mode
- //-----------------------------------------------------------------------
- /**
- * Find the most frequently occurring item.
- *
- * @param <T> type of values processed by this method
- * @param items to check
- * @return most populous T, {@code null} if non-unique or no items supplied
- * @since 3.0.1
- */
- public static <T> T mode(T... items) {
- if (ArrayUtils.isNotEmpty(items)) {
- HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(items.length);
- for (T t : items) {
- MutableInt count = occurrences.get(t);
- if (count == null) {
- occurrences.put(t, new MutableInt(1));
- } else {
- count.increment();
- }
- }
- T result = null;
- int max = 0;
- for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) {
- int cmp = e.getValue().intValue();
- if (cmp == max) {
- result = null;
- } else if (cmp > max) {
- max = cmp;
- result = e.getKey();
- }
- }
- return result;
- }
- return null;
- }
-
- // cloning
- //-----------------------------------------------------------------------
- /**
- * <p>Clone an object.</p>
- *
- * @param <T> the type of the object
- * @param obj the object to clone, null returns null
- * @return the clone if the object implements {@link Cloneable} otherwise {@code null}
- * @throws CloneFailedException if the object is cloneable and the clone operation fails
- * @since 3.0
- */
- public static <T> T clone(final T obj) {
- if (obj instanceof Cloneable) {
- final Object result;
- if (obj.getClass().isArray()) {
- final Class<?> componentType = obj.getClass().getComponentType();
- if (!componentType.isPrimitive()) {
- result = ((Object[]) obj).clone();
- } else {
- int length = Array.getLength(obj);
- result = Array.newInstance(componentType, length);
- while (length-- > 0) {
- Array.set(result, length, Array.get(obj, length));
- }
- }
- } else {
- try {
- final Method clone = obj.getClass().getMethod("clone");
- result = clone.invoke(obj);
- } catch (final NoSuchMethodException e) {
- throw new CloneFailedException("Cloneable type "
- + obj.getClass().getName()
- + " has no clone method", e);
- } catch (final IllegalAccessException e) {
- throw new CloneFailedException("Cannot clone Cloneable type "
- + obj.getClass().getName(), e);
- } catch (final InvocationTargetException e) {
- throw new CloneFailedException("Exception cloning Cloneable type "
- + obj.getClass().getName(), e.getCause());
- }
- }
- @SuppressWarnings("unchecked")
- final T checked = (T) result;
- return checked;
- }
-
- return null;
- }
-
- /**
- * <p>Clone an object if possible.</p>
- *
- * <p>This method is similar to {@link #clone(Object)}, but will return the provided
- * instance as the return value instead of {@code null} if the instance
- * is not cloneable. This is more convenient if the caller uses different
- * implementations (e.g. of a service) and some of the implementations do not allow concurrent
- * processing or have state. In such cases the implementation can simply provide a proper
- * clone implementation and the caller's code does not have to change.</p>
- *
- * @param <T> the type of the object
- * @param obj the object to clone, null returns null
- * @return the clone if the object implements {@link Cloneable} otherwise the object itself
- * @throws CloneFailedException if the object is cloneable and the clone operation fails
- * @since 3.0
- */
- public static <T> T cloneIfPossible(final T obj) {
- final T clone = clone(obj);
- return clone == null ? obj : clone;
- }
-
- // Null
- //-----------------------------------------------------------------------
- /**
- * <p>Class used as a null placeholder where {@code null}
- * has another meaning.</p>
- *
- * <p>For example, in a {@code HashMap} the
- * {@link java.util.HashMap#get(java.lang.Object)} method returns
- * {@code null} if the {@code Map} contains {@code null} or if there is
- * no matching key. The {@code Null} placeholder can be used to distinguish
- * between these two cases.</p>
- *
- * <p>Another example is {@code Hashtable}, where {@code null}
- * cannot be stored.</p>
- */
- public static class Null implements Serializable {
- /**
- * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 7092611880189329093L;
-
- /**
- * Restricted constructor - singleton.
- */
- Null() {
- super();
- }
-
- /**
- * <p>Ensure singleton.</p>
- *
- * @return the singleton value
- */
- private Object readResolve() {
- return ObjectUtils.NULL;
- }
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import org.apache.commons.lang3.exception.CloneFailedException; +import org.apache.commons.lang3.mutable.MutableInt; + +/** + * <p>Operations on {@code Object}.</p> + * + * <p>This class tries to handle {@code null} input gracefully. + * An exception will generally not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.</p> + * + * <p>#ThreadSafe#</p> + * @since 1.0 + * @version $Id: ObjectUtils.java 1153350 2011-08-03 05:29:21Z bayard $ + */ +//@Immutable +public class ObjectUtils { + + /** + * <p>Singleton used as a {@code null} placeholder where + * {@code null} has another meaning.</p> + * + * <p>For example, in a {@code HashMap} the + * {@link java.util.HashMap#get(java.lang.Object)} method returns + * {@code null} if the {@code Map} contains {@code null} or if there + * is no matching key. The {@code Null} placeholder can be used to + * distinguish between these two cases.</p> + * + * <p>Another example is {@code Hashtable}, where {@code null} + * cannot be stored.</p> + * + * <p>This instance is Serializable.</p> + */ + public static final Null NULL = new Null(); + + /** + * <p>{@code ObjectUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code ObjectUtils.defaultIfNull("a","b");}.</p> + * + * <p>This constructor is public to permit tools that require a JavaBean + * instance to operate.</p> + */ + public ObjectUtils() { + super(); + } + + // Defaulting + //----------------------------------------------------------------------- + /** + * <p>Returns a default value if the object passed is {@code null}.</p> + * + * <pre> + * ObjectUtils.defaultIfNull(null, null) = null + * ObjectUtils.defaultIfNull(null, "") = "" + * ObjectUtils.defaultIfNull(null, "zz") = "zz" + * ObjectUtils.defaultIfNull("abc", *) = "abc" + * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE + * </pre> + * + * @param <T> the type of the object + * @param object the {@code Object} to test, may be {@code null} + * @param defaultValue the default value to return, may be {@code null} + * @return {@code object} if it is not {@code null}, defaultValue otherwise + */ + public static <T> T defaultIfNull(T object, T defaultValue) { + return object != null ? object : defaultValue; + } + + /** + * <p>Returns the first value in the array which is not {@code null}. + * If all the values are {@code null} or the array is {@code null} + * or empty then {@code null} is returned.</p> + * + * <pre> + * ObjectUtils.firstNonNull(null, null) = null + * ObjectUtils.firstNonNull(null, "") = "" + * ObjectUtils.firstNonNull(null, null, "") = "" + * ObjectUtils.firstNonNull(null, "zz") = "zz" + * ObjectUtils.firstNonNull("abc", *) = "abc" + * ObjectUtils.firstNonNull(null, "xyz", *) = "xyz" + * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE + * ObjectUtils.firstNonNull() = null + * </pre> + * + * @param <T> the component type of the array + * @param values the values to test, may be {@code null} or empty + * @return the first value from {@code values} which is not {@code null}, + * or {@code null} if there are no non-null values + * @since 3.0 + */ + public static <T> T firstNonNull(T... values) { + if (values != null) { + for (T val : values) { + if (val != null) { + return val; + } + } + } + return null; + } + + // Null-safe equals/hashCode + //----------------------------------------------------------------------- + /** + * <p>Compares two objects for equality, where either one or both + * objects may be {@code null}.</p> + * + * <pre> + * ObjectUtils.equals(null, null) = true + * ObjectUtils.equals(null, "") = false + * ObjectUtils.equals("", null) = false + * ObjectUtils.equals("", "") = true + * ObjectUtils.equals(Boolean.TRUE, null) = false + * ObjectUtils.equals(Boolean.TRUE, "true") = false + * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE) = true + * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false + * </pre> + * + * @param object1 the first object, may be {@code null} + * @param object2 the second object, may be {@code null} + * @return {@code true} if the values of both objects are the same + */ + public static boolean equals(Object object1, Object object2) { + if (object1 == object2) { + return true; + } + if ((object1 == null) || (object2 == null)) { + return false; + } + return object1.equals(object2); + } + + /** + * <p>Compares two objects for inequality, where either one or both + * objects may be {@code null}.</p> + * + * <pre> + * ObjectUtils.notEqual(null, null) = false + * ObjectUtils.notEqual(null, "") = true + * ObjectUtils.notEqual("", null) = true + * ObjectUtils.notEqual("", "") = false + * ObjectUtils.notEqual(Boolean.TRUE, null) = true + * ObjectUtils.notEqual(Boolean.TRUE, "true") = true + * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE) = false + * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true + * </pre> + * + * @param object1 the first object, may be {@code null} + * @param object2 the second object, may be {@code null} + * @return {@code false} if the values of both objects are the same + */ + public static boolean notEqual(Object object1, Object object2) { + return ObjectUtils.equals(object1, object2) == false; + } + + /** + * <p>Gets the hash code of an object returning zero when the + * object is {@code null}.</p> + * + * <pre> + * ObjectUtils.hashCode(null) = 0 + * ObjectUtils.hashCode(obj) = obj.hashCode() + * </pre> + * + * @param obj the object to obtain the hash code of, may be {@code null} + * @return the hash code of the object, or zero if null + * @since 2.1 + */ + public static int hashCode(Object obj) { + // hashCode(Object) retained for performance, as hash code is often critical + return (obj == null) ? 0 : obj.hashCode(); + } + + /** + * <p>Gets the hash code for multiple objects.</p> + * + * <p>This allows a hash code to be rapidly calculated for a number of objects. + * The hash code for a single object is the <em>not</em> same as {@link #hashCode(Object)}. + * The hash code for multiple objects is the same as that calculated by an + * {@code ArrayList} containing the specified objects.</p> + * + * <pre> + * ObjectUtils.hashCodeMulti() = 1 + * ObjectUtils.hashCodeMulti((Object[]) null) = 1 + * ObjectUtils.hashCodeMulti(a) = 31 + a.hashCode() + * ObjectUtils.hashCodeMulti(a,b) = (31 + a.hashCode()) * 31 + b.hashCode() + * ObjectUtils.hashCodeMulti(a,b,c) = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode() + * </pre> + * + * @param objects the objects to obtain the hash code of, may be {@code null} + * @return the hash code of the objects, or zero if null + * @since 3.0 + */ + public static int hashCodeMulti(Object... objects) { + int hash = 1; + if (objects != null) { + for (Object object : objects) { + hash = hash * 31 + ObjectUtils.hashCode(object); + } + } + return hash; + } + + // Identity ToString + //----------------------------------------------------------------------- + /** + * <p>Gets the toString that would be produced by {@code Object} + * if a class did not override toString itself. {@code null} + * will return {@code null}.</p> + * + * <pre> + * ObjectUtils.identityToString(null) = null + * ObjectUtils.identityToString("") = "java.lang.String@1e23" + * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa" + * </pre> + * + * @param object the object to create a toString for, may be + * {@code null} + * @return the default toString text, or {@code null} if + * {@code null} passed in + */ + public static String identityToString(Object object) { + if (object == null) { + return null; + } + StringBuffer buffer = new StringBuffer(); + identityToString(buffer, object); + return buffer.toString(); + } + + /** + * <p>Appends the toString that would be produced by {@code Object} + * if a class did not override toString itself. {@code null} + * will throw a NullPointerException for either of the two parameters. </p> + * + * <pre> + * ObjectUtils.identityToString(buf, "") = buf.append("java.lang.String@1e23" + * ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa" + * ObjectUtils.identityToString(buf, Boolean.TRUE) = buf.append("java.lang.Boolean@7fa") + * </pre> + * + * @param buffer the buffer to append to + * @param object the object to create a toString for + * @since 2.4 + */ + public static void identityToString(StringBuffer buffer, Object object) { + if (object == null) { + throw new NullPointerException("Cannot get the toString of a null identity"); + } + buffer.append(object.getClass().getName()) + .append('@') + .append(Integer.toHexString(System.identityHashCode(object))); + } + + // ToString + //----------------------------------------------------------------------- + /** + * <p>Gets the {@code toString} of an {@code Object} returning + * an empty string ("") if {@code null} input.</p> + * + * <pre> + * ObjectUtils.toString(null) = "" + * ObjectUtils.toString("") = "" + * ObjectUtils.toString("bat") = "bat" + * ObjectUtils.toString(Boolean.TRUE) = "true" + * </pre> + * + * @see StringUtils#defaultString(String) + * @see String#valueOf(Object) + * @param obj the Object to {@code toString}, may be null + * @return the passed in Object's toString, or nullStr if {@code null} input + * @since 2.0 + */ + public static String toString(Object obj) { + return obj == null ? "" : obj.toString(); + } + + /** + * <p>Gets the {@code toString} of an {@code Object} returning + * a specified text if {@code null} input.</p> + * + * <pre> + * ObjectUtils.toString(null, null) = null + * ObjectUtils.toString(null, "null") = "null" + * ObjectUtils.toString("", "null") = "" + * ObjectUtils.toString("bat", "null") = "bat" + * ObjectUtils.toString(Boolean.TRUE, "null") = "true" + * </pre> + * + * @see StringUtils#defaultString(String,String) + * @see String#valueOf(Object) + * @param obj the Object to {@code toString}, may be null + * @param nullStr the String to return if {@code null} input, may be null + * @return the passed in Object's toString, or nullStr if {@code null} input + * @since 2.0 + */ + public static String toString(Object obj, String nullStr) { + return obj == null ? nullStr : obj.toString(); + } + + // Comparable + //----------------------------------------------------------------------- + /** + * <p>Null safe comparison of Comparables.</p> + * + * @param <T> type of the values processed by this method + * @param values the set of comparable values, may be null + * @return + * <ul> + * <li>If any objects are non-null and unequal, the lesser object. + * <li>If all objects are non-null and equal, the first. + * <li>If any of the comparables are null, the lesser of the non-null objects. + * <li>If all the comparables are null, null is returned. + * </ul> + */ + public static <T extends Comparable<? super T>> T min(T... values) { + T result = null; + if (values != null) { + for (T value : values) { + if (compare(value, result, true) < 0) { + result = value; + } + } + } + return result; + } + + /** + * <p>Null safe comparison of Comparables.</p> + * + * @param <T> type of the values processed by this method + * @param values the set of comparable values, may be null + * @return + * <ul> + * <li>If any objects are non-null and unequal, the greater object. + * <li>If all objects are non-null and equal, the first. + * <li>If any of the comparables are null, the greater of the non-null objects. + * <li>If all the comparables are null, null is returned. + * </ul> + */ + public static <T extends Comparable<? super T>> T max(T... values) { + T result = null; + if (values != null) { + for (T value : values) { + if (compare(value, result, false) > 0) { + result = value; + } + } + } + return result; + } + + /** + * <p>Null safe comparison of Comparables. + * {@code null} is assumed to be less than a non-{@code null} value.</p> + * + * @param <T> type of the values processed by this method + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + */ + public static <T extends Comparable<? super T>> int compare(T c1, T c2) { + return compare(c1, c2, false); + } + + /** + * <p>Null safe comparison of Comparables.</p> + * + * @param <T> type of the values processed by this method + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @param nullGreater if true {@code null} is considered greater + * than a non-{@code null} value or if false {@code null} is + * considered less than a Non-{@code null} value + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + * @see java.util.Comparator#compare(Object, Object) + */ + public static <T extends Comparable<? super T>> int compare(T c1, T c2, boolean nullGreater) { + if (c1 == c2) { + return 0; + } else if (c1 == null) { + return (nullGreater ? 1 : -1); + } else if (c2 == null) { + return (nullGreater ? -1 : 1); + } + return c1.compareTo(c2); + } + + /** + * Find the "best guess" middle value among comparables. If there is an even + * number of total values, the lower of the two middle values will be returned. + * @param <T> type of values processed by this method + * @param items to compare + * @return T at middle position + * @throws NullPointerException if items is {@code null} + * @throws IllegalArgumentException if items is empty or contains {@code null} values + * @since 3.0.1 + */ + public static <T extends Comparable<? super T>> T median(T... items) { + Validate.notEmpty(items); + Validate.noNullElements(items); + TreeSet<T> sort = new TreeSet<T>(); + Collections.addAll(sort, items); + @SuppressWarnings("unchecked") //we know all items added were T instances + T result = (T) sort.toArray()[(sort.size() - 1) / 2]; + return result; + } + + /** + * Find the "best guess" middle value among comparables. If there is an even + * number of total values, the lower of the two middle values will be returned. + * @param <T> type of values processed by this method + * @param comparator to use for comparisons + * @param items to compare + * @return T at middle position + * @throws NullPointerException if items or comparator is {@code null} + * @throws IllegalArgumentException if items is empty or contains {@code null} values + * @since 3.0.1 + */ + public static <T> T median(Comparator<T> comparator, T... items) { + Validate.notEmpty(items, "null/empty items"); + Validate.noNullElements(items); + Validate.notNull(comparator, "null comparator"); + TreeSet<T> sort = new TreeSet<T>(comparator); + Collections.addAll(sort, items); + @SuppressWarnings("unchecked") //we know all items added were T instances + T result = (T) sort.toArray()[(sort.size() - 1) / 2]; + return result; + } + + // Mode + //----------------------------------------------------------------------- + /** + * Find the most frequently occurring item. + * + * @param <T> type of values processed by this method + * @param items to check + * @return most populous T, {@code null} if non-unique or no items supplied + * @since 3.0.1 + */ + public static <T> T mode(T... items) { + if (ArrayUtils.isNotEmpty(items)) { + HashMap<T, MutableInt> occurrences = new HashMap<T, MutableInt>(items.length); + for (T t : items) { + MutableInt count = occurrences.get(t); + if (count == null) { + occurrences.put(t, new MutableInt(1)); + } else { + count.increment(); + } + } + T result = null; + int max = 0; + for (Map.Entry<T, MutableInt> e : occurrences.entrySet()) { + int cmp = e.getValue().intValue(); + if (cmp == max) { + result = null; + } else if (cmp > max) { + max = cmp; + result = e.getKey(); + } + } + return result; + } + return null; + } + + // cloning + //----------------------------------------------------------------------- + /** + * <p>Clone an object.</p> + * + * @param <T> the type of the object + * @param obj the object to clone, null returns null + * @return the clone if the object implements {@link Cloneable} otherwise {@code null} + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 3.0 + */ + public static <T> T clone(final T obj) { + if (obj instanceof Cloneable) { + final Object result; + if (obj.getClass().isArray()) { + final Class<?> componentType = obj.getClass().getComponentType(); + if (!componentType.isPrimitive()) { + result = ((Object[]) obj).clone(); + } else { + int length = Array.getLength(obj); + result = Array.newInstance(componentType, length); + while (length-- > 0) { + Array.set(result, length, Array.get(obj, length)); + } + } + } else { + try { + final Method clone = obj.getClass().getMethod("clone"); + result = clone.invoke(obj); + } catch (final NoSuchMethodException e) { + throw new CloneFailedException("Cloneable type " + + obj.getClass().getName() + + " has no clone method", e); + } catch (final IllegalAccessException e) { + throw new CloneFailedException("Cannot clone Cloneable type " + + obj.getClass().getName(), e); + } catch (final InvocationTargetException e) { + throw new CloneFailedException("Exception cloning Cloneable type " + + obj.getClass().getName(), e.getCause()); + } + } + @SuppressWarnings("unchecked") + final T checked = (T) result; + return checked; + } + + return null; + } + + /** + * <p>Clone an object if possible.</p> + * + * <p>This method is similar to {@link #clone(Object)}, but will return the provided + * instance as the return value instead of {@code null} if the instance + * is not cloneable. This is more convenient if the caller uses different + * implementations (e.g. of a service) and some of the implementations do not allow concurrent + * processing or have state. In such cases the implementation can simply provide a proper + * clone implementation and the caller's code does not have to change.</p> + * + * @param <T> the type of the object + * @param obj the object to clone, null returns null + * @return the clone if the object implements {@link Cloneable} otherwise the object itself + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 3.0 + */ + public static <T> T cloneIfPossible(final T obj) { + final T clone = clone(obj); + return clone == null ? obj : clone; + } + + // Null + //----------------------------------------------------------------------- + /** + * <p>Class used as a null placeholder where {@code null} + * has another meaning.</p> + * + * <p>For example, in a {@code HashMap} the + * {@link java.util.HashMap#get(java.lang.Object)} method returns + * {@code null} if the {@code Map} contains {@code null} or if there is + * no matching key. The {@code Null} placeholder can be used to distinguish + * between these two cases.</p> + * + * <p>Another example is {@code Hashtable}, where {@code null} + * cannot be stored.</p> + */ + public static class Null implements Serializable { + /** + * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0 + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7092611880189329093L; + + /** + * Restricted constructor - singleton. + */ + Null() { + super(); + } + + /** + * <p>Ensure singleton.</p> + * + * @return the singleton value + */ + private Object readResolve() { + return ObjectUtils.NULL; + } + } + +} diff --git a/src/org/apache/commons/lang3/StringUtils.java b/src/org/apache/commons/lang3/StringUtils.java index 9ea5de4..807f4d6 100644 --- a/src/org/apache/commons/lang3/StringUtils.java +++ b/src/org/apache/commons/lang3/StringUtils.java @@ -1,6525 +1,6525 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-/**
- * <p>Operations on {@link java.lang.String} that are
- * {@code null} safe.</p>
- *
- * <ul>
- * <li><b>IsEmpty/IsBlank</b>
- * - checks if a String contains text</li>
- * <li><b>Trim/Strip</b>
- * - removes leading and trailing whitespace</li>
- * <li><b>Equals</b>
- * - compares two strings null-safe</li>
- * <li><b>startsWith</b>
- * - check if a String starts with a prefix null-safe</li>
- * <li><b>endsWith</b>
- * - check if a String ends with a suffix null-safe</li>
- * <li><b>IndexOf/LastIndexOf/Contains</b>
- * - null-safe index-of checks
- * <li><b>IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut</b>
- * - index-of any of a set of Strings</li>
- * <li><b>ContainsOnly/ContainsNone/ContainsAny</b>
- * - does String contains only/none/any of these characters</li>
- * <li><b>Substring/Left/Right/Mid</b>
- * - null-safe substring extractions</li>
- * <li><b>SubstringBefore/SubstringAfter/SubstringBetween</b>
- * - substring extraction relative to other strings</li>
- * <li><b>Split/Join</b>
- * - splits a String into an array of substrings and vice versa</li>
- * <li><b>Remove/Delete</b>
- * - removes part of a String</li>
- * <li><b>Replace/Overlay</b>
- * - Searches a String and replaces one String with another</li>
- * <li><b>Chomp/Chop</b>
- * - removes the last part of a String</li>
- * <li><b>LeftPad/RightPad/Center/Repeat</b>
- * - pads a String</li>
- * <li><b>UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize</b>
- * - changes the case of a String</li>
- * <li><b>CountMatches</b>
- * - counts the number of occurrences of one String in another</li>
- * <li><b>IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable</b>
- * - checks the characters in a String</li>
- * <li><b>DefaultString</b>
- * - protects against a null input String</li>
- * <li><b>Reverse/ReverseDelimited</b>
- * - reverses a String</li>
- * <li><b>Abbreviate</b>
- * - abbreviates a string using ellipsis</li>
- * <li><b>Difference</b>
- * - compares Strings and reports on their differences</li>
- * <li><b>LevenshteinDistance</b>
- * - the number of changes needed to change one String into another</li>
- * </ul>
- *
- * <p>The {@code StringUtils} class defines certain words related to
- * String handling.</p>
- *
- * <ul>
- * <li>null - {@code null}</li>
- * <li>empty - a zero-length string ({@code ""})</li>
- * <li>space - the space character ({@code ' '}, char 32)</li>
- * <li>whitespace - the characters defined by {@link Character#isWhitespace(char)}</li>
- * <li>trim - the characters <= 32 as in {@link String#trim()}</li>
- * </ul>
- *
- * <p>{@code StringUtils} handles {@code null} input Strings quietly.
- * That is to say that a {@code null} input will return {@code null}.
- * Where a {@code boolean} or {@code int} is being returned
- * details vary by method.</p>
- *
- * <p>A side effect of the {@code null} handling is that a
- * {@code NullPointerException} should be considered a bug in
- * {@code StringUtils}.</p>
- *
- * <p>Methods in this class give sample code to explain their operation.
- * The symbol {@code *} is used to indicate any input including {@code null}.</p>
- *
- * <p>#ThreadSafe#</p>
- * @see java.lang.String
- * @since 1.0
- * @version $Id$
- */
-//@Immutable
-public class StringUtils {
- // Performance testing notes (JDK 1.4, Jul03, scolebourne)
- // Whitespace:
- // Character.isWhitespace() is faster than WHITESPACE.indexOf()
- // where WHITESPACE is a string of all whitespace characters
- //
- // Character access:
- // String.charAt(n) versus toCharArray(), then array[n]
- // String.charAt(n) is about 15% worse for a 10K string
- // They are about equal for a length 50 string
- // String.charAt(n) is about 4 times better for a length 3 string
- // String.charAt(n) is best bet overall
- //
- // Append:
- // String.concat about twice as fast as StringBuffer.append
- // (not sure who tested this)
-
- /**
- * The empty String {@code ""}.
- * @since 2.0
- */
- public static final String EMPTY = "";
-
- /**
- * Represents a failed index search.
- * @since 2.1
- */
- public static final int INDEX_NOT_FOUND = -1;
-
- /**
- * <p>The maximum size to which the padding constant(s) can expand.</p>
- */
- private static final int PAD_LIMIT = 8192;
-
- /**
- * A regex pattern for recognizing blocks of whitespace characters.
- */
- private static final Pattern WHITESPACE_BLOCK = Pattern.compile("\\s+");
-
- /**
- * <p>{@code StringUtils} instances should NOT be constructed in
- * standard programming. Instead, the class should be used as
- * {@code StringUtils.trim(" foo ");}.</p>
- *
- * <p>This constructor is public to permit tools that require a JavaBean
- * instance to operate.</p>
- */
- public StringUtils() {
- super();
- }
-
- // Empty checks
- //-----------------------------------------------------------------------
- /**
- * <p>Checks if a CharSequence is empty ("") or null.</p>
- *
- * <pre>
- * StringUtils.isEmpty(null) = true
- * StringUtils.isEmpty("") = true
- * StringUtils.isEmpty(" ") = false
- * StringUtils.isEmpty("bob") = false
- * StringUtils.isEmpty(" bob ") = false
- * </pre>
- *
- * <p>NOTE: This method changed in Lang version 2.0.
- * It no longer trims the CharSequence.
- * That functionality is available in isBlank().</p>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if the CharSequence is empty or null
- * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
- */
- public static boolean isEmpty(CharSequence cs) {
- return cs == null || cs.length() == 0;
- }
-
- /**
- * <p>Checks if a CharSequence is not empty ("") and not null.</p>
- *
- * <pre>
- * StringUtils.isNotEmpty(null) = false
- * StringUtils.isNotEmpty("") = false
- * StringUtils.isNotEmpty(" ") = true
- * StringUtils.isNotEmpty("bob") = true
- * StringUtils.isNotEmpty(" bob ") = true
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if the CharSequence is not empty and not null
- * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence)
- */
- public static boolean isNotEmpty(CharSequence cs) {
- return !StringUtils.isEmpty(cs);
- }
-
- /**
- * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p>
- *
- * <pre>
- * StringUtils.isBlank(null) = true
- * StringUtils.isBlank("") = true
- * StringUtils.isBlank(" ") = true
- * StringUtils.isBlank("bob") = false
- * StringUtils.isBlank(" bob ") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if the CharSequence is null, empty or whitespace
- * @since 2.0
- * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence)
- */
- public static boolean isBlank(CharSequence cs) {
- int strLen;
- if (cs == null || (strLen = cs.length()) == 0) {
- return true;
- }
- for (int i = 0; i < strLen; i++) {
- if ((Character.isWhitespace(cs.charAt(i)) == false)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p>
- *
- * <pre>
- * StringUtils.isNotBlank(null) = false
- * StringUtils.isNotBlank("") = false
- * StringUtils.isNotBlank(" ") = false
- * StringUtils.isNotBlank("bob") = true
- * StringUtils.isNotBlank(" bob ") = true
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if the CharSequence is
- * not empty and not null and not whitespace
- * @since 2.0
- * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence)
- */
- public static boolean isNotBlank(CharSequence cs) {
- return !StringUtils.isBlank(cs);
- }
-
- // Trim
- //-----------------------------------------------------------------------
- /**
- * <p>Removes control characters (char <= 32) from both
- * ends of this String, handling {@code null} by returning
- * {@code null}.</p>
- *
- * <p>The String is trimmed using {@link String#trim()}.
- * Trim removes start and end characters <= 32.
- * To strip whitespace use {@link #strip(String)}.</p>
- *
- * <p>To trim your choice of characters, use the
- * {@link #strip(String, String)} methods.</p>
- *
- * <pre>
- * StringUtils.trim(null) = null
- * StringUtils.trim("") = ""
- * StringUtils.trim(" ") = ""
- * StringUtils.trim("abc") = "abc"
- * StringUtils.trim(" abc ") = "abc"
- * </pre>
- *
- * @param str the String to be trimmed, may be null
- * @return the trimmed string, {@code null} if null String input
- */
- public static String trim(String str) {
- return str == null ? null : str.trim();
- }
-
- /**
- * <p>Removes control characters (char <= 32) from both
- * ends of this String returning {@code null} if the String is
- * empty ("") after the trim or if it is {@code null}.
- *
- * <p>The String is trimmed using {@link String#trim()}.
- * Trim removes start and end characters <= 32.
- * To strip whitespace use {@link #stripToNull(String)}.</p>
- *
- * <pre>
- * StringUtils.trimToNull(null) = null
- * StringUtils.trimToNull("") = null
- * StringUtils.trimToNull(" ") = null
- * StringUtils.trimToNull("abc") = "abc"
- * StringUtils.trimToNull(" abc ") = "abc"
- * </pre>
- *
- * @param str the String to be trimmed, may be null
- * @return the trimmed String,
- * {@code null} if only chars <= 32, empty or null String input
- * @since 2.0
- */
- public static String trimToNull(String str) {
- String ts = trim(str);
- return isEmpty(ts) ? null : ts;
- }
-
- /**
- * <p>Removes control characters (char <= 32) from both
- * ends of this String returning an empty String ("") if the String
- * is empty ("") after the trim or if it is {@code null}.
- *
- * <p>The String is trimmed using {@link String#trim()}.
- * Trim removes start and end characters <= 32.
- * To strip whitespace use {@link #stripToEmpty(String)}.</p>
- *
- * <pre>
- * StringUtils.trimToEmpty(null) = ""
- * StringUtils.trimToEmpty("") = ""
- * StringUtils.trimToEmpty(" ") = ""
- * StringUtils.trimToEmpty("abc") = "abc"
- * StringUtils.trimToEmpty(" abc ") = "abc"
- * </pre>
- *
- * @param str the String to be trimmed, may be null
- * @return the trimmed String, or an empty String if {@code null} input
- * @since 2.0
- */
- public static String trimToEmpty(String str) {
- return str == null ? EMPTY : str.trim();
- }
-
- // Stripping
- //-----------------------------------------------------------------------
- /**
- * <p>Strips whitespace from the start and end of a String.</p>
- *
- * <p>This is similar to {@link #trim(String)} but removes whitespace.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.strip(null) = null
- * StringUtils.strip("") = ""
- * StringUtils.strip(" ") = ""
- * StringUtils.strip("abc") = "abc"
- * StringUtils.strip(" abc") = "abc"
- * StringUtils.strip("abc ") = "abc"
- * StringUtils.strip(" abc ") = "abc"
- * StringUtils.strip(" ab c ") = "ab c"
- * </pre>
- *
- * @param str the String to remove whitespace from, may be null
- * @return the stripped String, {@code null} if null String input
- */
- public static String strip(String str) {
- return strip(str, null);
- }
-
- /**
- * <p>Strips whitespace from the start and end of a String returning
- * {@code null} if the String is empty ("") after the strip.</p>
- *
- * <p>This is similar to {@link #trimToNull(String)} but removes whitespace.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.stripToNull(null) = null
- * StringUtils.stripToNull("") = null
- * StringUtils.stripToNull(" ") = null
- * StringUtils.stripToNull("abc") = "abc"
- * StringUtils.stripToNull(" abc") = "abc"
- * StringUtils.stripToNull("abc ") = "abc"
- * StringUtils.stripToNull(" abc ") = "abc"
- * StringUtils.stripToNull(" ab c ") = "ab c"
- * </pre>
- *
- * @param str the String to be stripped, may be null
- * @return the stripped String,
- * {@code null} if whitespace, empty or null String input
- * @since 2.0
- */
- public static String stripToNull(String str) {
- if (str == null) {
- return null;
- }
- str = strip(str, null);
- return str.length() == 0 ? null : str;
- }
-
- /**
- * <p>Strips whitespace from the start and end of a String returning
- * an empty String if {@code null} input.</p>
- *
- * <p>This is similar to {@link #trimToEmpty(String)} but removes whitespace.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.stripToEmpty(null) = ""
- * StringUtils.stripToEmpty("") = ""
- * StringUtils.stripToEmpty(" ") = ""
- * StringUtils.stripToEmpty("abc") = "abc"
- * StringUtils.stripToEmpty(" abc") = "abc"
- * StringUtils.stripToEmpty("abc ") = "abc"
- * StringUtils.stripToEmpty(" abc ") = "abc"
- * StringUtils.stripToEmpty(" ab c ") = "ab c"
- * </pre>
- *
- * @param str the String to be stripped, may be null
- * @return the trimmed String, or an empty String if {@code null} input
- * @since 2.0
- */
- public static String stripToEmpty(String str) {
- return str == null ? EMPTY : strip(str, null);
- }
-
- /**
- * <p>Strips any of a set of characters from the start and end of a String.
- * This is similar to {@link String#trim()} but allows the characters
- * to be stripped to be controlled.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * An empty string ("") input returns the empty string.</p>
- *
- * <p>If the stripChars String is {@code null}, whitespace is
- * stripped as defined by {@link Character#isWhitespace(char)}.
- * Alternatively use {@link #strip(String)}.</p>
- *
- * <pre>
- * StringUtils.strip(null, *) = null
- * StringUtils.strip("", *) = ""
- * StringUtils.strip("abc", null) = "abc"
- * StringUtils.strip(" abc", null) = "abc"
- * StringUtils.strip("abc ", null) = "abc"
- * StringUtils.strip(" abc ", null) = "abc"
- * StringUtils.strip(" abcyx", "xyz") = " abc"
- * </pre>
- *
- * @param str the String to remove characters from, may be null
- * @param stripChars the characters to remove, null treated as whitespace
- * @return the stripped String, {@code null} if null String input
- */
- public static String strip(String str, String stripChars) {
- if (isEmpty(str)) {
- return str;
- }
- str = stripStart(str, stripChars);
- return stripEnd(str, stripChars);
- }
-
- /**
- * <p>Strips any of a set of characters from the start of a String.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * An empty string ("") input returns the empty string.</p>
- *
- * <p>If the stripChars String is {@code null}, whitespace is
- * stripped as defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.stripStart(null, *) = null
- * StringUtils.stripStart("", *) = ""
- * StringUtils.stripStart("abc", "") = "abc"
- * StringUtils.stripStart("abc", null) = "abc"
- * StringUtils.stripStart(" abc", null) = "abc"
- * StringUtils.stripStart("abc ", null) = "abc "
- * StringUtils.stripStart(" abc ", null) = "abc "
- * StringUtils.stripStart("yxabc ", "xyz") = "abc "
- * </pre>
- *
- * @param str the String to remove characters from, may be null
- * @param stripChars the characters to remove, null treated as whitespace
- * @return the stripped String, {@code null} if null String input
- */
- public static String stripStart(String str, String stripChars) {
- int strLen;
- if (str == null || (strLen = str.length()) == 0) {
- return str;
- }
- int start = 0;
- if (stripChars == null) {
- while ((start != strLen) && Character.isWhitespace(str.charAt(start))) {
- start++;
- }
- } else if (stripChars.length() == 0) {
- return str;
- } else {
- while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) {
- start++;
- }
- }
- return str.substring(start);
- }
-
- /**
- * <p>Strips any of a set of characters from the end of a String.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * An empty string ("") input returns the empty string.</p>
- *
- * <p>If the stripChars String is {@code null}, whitespace is
- * stripped as defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.stripEnd(null, *) = null
- * StringUtils.stripEnd("", *) = ""
- * StringUtils.stripEnd("abc", "") = "abc"
- * StringUtils.stripEnd("abc", null) = "abc"
- * StringUtils.stripEnd(" abc", null) = " abc"
- * StringUtils.stripEnd("abc ", null) = "abc"
- * StringUtils.stripEnd(" abc ", null) = " abc"
- * StringUtils.stripEnd(" abcyx", "xyz") = " abc"
- * StringUtils.stripEnd("120.00", ".0") = "12"
- * </pre>
- *
- * @param str the String to remove characters from, may be null
- * @param stripChars the set of characters to remove, null treated as whitespace
- * @return the stripped String, {@code null} if null String input
- */
- public static String stripEnd(String str, String stripChars) {
- int end;
- if (str == null || (end = str.length()) == 0) {
- return str;
- }
-
- if (stripChars == null) {
- while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) {
- end--;
- }
- } else if (stripChars.length() == 0) {
- return str;
- } else {
- while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) {
- end--;
- }
- }
- return str.substring(0, end);
- }
-
- // StripAll
- //-----------------------------------------------------------------------
- /**
- * <p>Strips whitespace from the start and end of every String in an array.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <p>A new array is returned each time, except for length zero.
- * A {@code null} array will return {@code null}.
- * An empty array will return itself.
- * A {@code null} array entry will be ignored.</p>
- *
- * <pre>
- * StringUtils.stripAll(null) = null
- * StringUtils.stripAll([]) = []
- * StringUtils.stripAll(["abc", " abc"]) = ["abc", "abc"]
- * StringUtils.stripAll(["abc ", null]) = ["abc", null]
- * </pre>
- *
- * @param strs the array to remove whitespace from, may be null
- * @return the stripped Strings, {@code null} if null array input
- */
- public static String[] stripAll(String... strs) {
- return stripAll(strs, null);
- }
-
- /**
- * <p>Strips any of a set of characters from the start and end of every
- * String in an array.</p>
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <p>A new array is returned each time, except for length zero.
- * A {@code null} array will return {@code null}.
- * An empty array will return itself.
- * A {@code null} array entry will be ignored.
- * A {@code null} stripChars will strip whitespace as defined by
- * {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.stripAll(null, *) = null
- * StringUtils.stripAll([], *) = []
- * StringUtils.stripAll(["abc", " abc"], null) = ["abc", "abc"]
- * StringUtils.stripAll(["abc ", null], null) = ["abc", null]
- * StringUtils.stripAll(["abc ", null], "yz") = ["abc ", null]
- * StringUtils.stripAll(["yabcz", null], "yz") = ["abc", null]
- * </pre>
- *
- * @param strs the array to remove characters from, may be null
- * @param stripChars the characters to remove, null treated as whitespace
- * @return the stripped Strings, {@code null} if null array input
- */
- public static String[] stripAll(String[] strs, String stripChars) {
- int strsLen;
- if (strs == null || (strsLen = strs.length) == 0) {
- return strs;
- }
- String[] newArr = new String[strsLen];
- for (int i = 0; i < strsLen; i++) {
- newArr[i] = strip(strs[i], stripChars);
- }
- return newArr;
- }
-
- /**
- * <p>Removes diacritics (~= accents) from a string. The case will not be altered.</p>
- * <p>For instance, 'à' will be replaced by 'a'.</p>
- * <p>Note that ligatures will be left as is.</p>
- *
- * <p>This method will use the first available implementation of:
- * Java 6's {@link java.text.Normalizer}, Java 1.3–1.5's {@code sun.text.Normalizer}</p>
- *
- * <pre>
- * StringUtils.stripAccents(null) = null
- * StringUtils.stripAccents("") = ""
- * StringUtils.stripAccents("control") = "control"
- * StringUtils.stripAccents("éclair") = "eclair"
- * </pre>
- *
- * @param input String to be stripped
- * @return input text with diacritics removed
- *
- * @since 3.0
- */
- // See also Lucene's ASCIIFoldingFilter (Lucene 2.9) that replaces accented characters by their unaccented equivalent (and uncommitted bug fix: https://issues.apache.org/jira/browse/LUCENE-1343?focusedCommentId=12858907&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12858907).
- public static String stripAccents(String input) {
- if(input == null) {
- return null;
- }
- try {
- String result = null;
- if (java6Available) {
- result = removeAccentsJava6(input);
- } else if (sunAvailable) {
- result = removeAccentsSUN(input);
- } else {
- throw new UnsupportedOperationException(
- "The stripAccents(CharSequence) method requires at least Java 1.6 or a Sun JVM");
- }
- // Note that none of the above methods correctly remove ligatures...
- return result;
- } catch(IllegalArgumentException iae) {
- throw new RuntimeException("IllegalArgumentException occurred", iae);
- } catch(IllegalAccessException iae) {
- throw new RuntimeException("IllegalAccessException occurred", iae);
- } catch(InvocationTargetException ite) {
- throw new RuntimeException("InvocationTargetException occurred", ite);
- } catch(SecurityException se) {
- throw new RuntimeException("SecurityException occurred", se);
- }
- }
-
- /**
- * Use {@code java.text.Normalizer#normalize(CharSequence, Normalizer.Form)}
- * (but be careful, this class exists in Java 1.3, with an entirely different meaning!)
- *
- * @param text the text to be processed
- * @return the processed string
- * @throws IllegalAccessException may be thrown by a reflection call
- * @throws InvocationTargetException if a reflection call throws an exception
- * @throws IllegalStateException if the {@code Normalizer} class is not available
- */
- private static String removeAccentsJava6(CharSequence text)
- throws IllegalAccessException, InvocationTargetException {
- /*
- String decomposed = java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD);
- return java6Pattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$
- */
- if (!java6Available || java6NormalizerFormNFD == null) {
- throw new IllegalStateException("java.text.Normalizer is not available");
- }
- String result;
- result = (String) java6NormalizeMethod.invoke(null, new Object[] {text, java6NormalizerFormNFD});
- result = java6Pattern.matcher(result).replaceAll("");//$NON-NLS-1$
- return result;
- }
-
- /**
- * Use {@code sun.text.Normalizer#decompose(String, boolean, int)}
- *
- * @param text the text to be processed
- * @return the processed string
- * @throws IllegalAccessException may be thrown by a reflection call
- * @throws InvocationTargetException if a reflection call throws an exception
- * @throws IllegalStateException if the {@code Normalizer} class is not available
- */
- private static String removeAccentsSUN(CharSequence text)
- throws IllegalAccessException, InvocationTargetException {
- /*
- String decomposed = sun.text.Normalizer.decompose(text, false, 0);
- return sunPattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$
- */
- if (! sunAvailable) {
- throw new IllegalStateException("sun.text.Normalizer is not available");
- }
- String result;
- result = (String) sunDecomposeMethod.invoke(null, new Object[] {text, Boolean.FALSE, Integer.valueOf(0)});
- result = sunPattern.matcher(result).replaceAll("");//$NON-NLS-1$
- return result;
- }
-
- // SUN internal, Java 1.3 -> Java 5
- private static boolean sunAvailable = false;
- private static Method sunDecomposeMethod = null;
- private static final Pattern sunPattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");//$NON-NLS-1$
- // Java 6+
- private static boolean java6Available = false;
- private static Method java6NormalizeMethod = null;
- private static Object java6NormalizerFormNFD = null;
- private static final Pattern java6Pattern = sunPattern;
-
- static {
- try {
- // java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD);
- // Be careful not to get Java 1.3 java.text.Normalizer!
- Class<?> normalizerFormClass = Thread.currentThread().getContextClassLoader()
- .loadClass("java.text.Normalizer$Form");//$NON-NLS-1$
- java6NormalizerFormNFD = normalizerFormClass.getField("NFD").get(null);//$NON-NLS-1$
- Class<?> normalizerClass = Thread.currentThread().getContextClassLoader()
- .loadClass("java.text.Normalizer");//$NON-NLS-1$
- java6NormalizeMethod = normalizerClass.getMethod("normalize",
- new Class[] {CharSequence.class, normalizerFormClass});//$NON-NLS-1$
- java6Available = true;
- } catch (ClassNotFoundException e) {
- java6Available = false;
- } catch (NoSuchFieldException e) {
- java6Available = false;
- } catch (IllegalAccessException e) {
- java6Available = false;
- } catch (NoSuchMethodException e) {
- java6Available = false;
- }
-
- try {
- // sun.text.Normalizer.decompose(text, false, 0);
- Class<?> normalizerClass = Thread.currentThread().getContextClassLoader()
- .loadClass("sun.text.Normalizer");//$NON-NLS-1$
- sunDecomposeMethod = normalizerClass.getMethod("decompose",
- new Class[] {String.class, Boolean.TYPE, Integer.TYPE});//$NON-NLS-1$
- sunAvailable = true;
- } catch (ClassNotFoundException e) {
- sunAvailable = false;
- } catch (NoSuchMethodException e) {
- sunAvailable = false;
- } catch (java.security.AccessControlException e) {
- // LANG-744 - thrown in Google App Engine
- sunAvailable = false;
- }
- }
-
- // Equals
- //-----------------------------------------------------------------------
- /**
- * <p>Compares two CharSequences, returning {@code true} if they are equal.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered to be equal. The comparison is case sensitive.</p>
- *
- * <pre>
- * StringUtils.equals(null, null) = true
- * StringUtils.equals(null, "abc") = false
- * StringUtils.equals("abc", null) = false
- * StringUtils.equals("abc", "abc") = true
- * StringUtils.equals("abc", "ABC") = false
- * </pre>
- *
- * @see java.lang.String#equals(Object)
- * @param cs1 the first CharSequence, may be null
- * @param cs2 the second CharSequence, may be null
- * @return {@code true} if the CharSequences are equal, case sensitive, or
- * both {@code null}
- * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence)
- */
- public static boolean equals(CharSequence cs1, CharSequence cs2) {
- return cs1 == null ? cs2 == null : cs1.equals(cs2);
- }
-
- /**
- * <p>Compares two CharSequences, returning {@code true} if they are equal ignoring
- * the case.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered equal. Comparison is case insensitive.</p>
- *
- * <pre>
- * StringUtils.equalsIgnoreCase(null, null) = true
- * StringUtils.equalsIgnoreCase(null, "abc") = false
- * StringUtils.equalsIgnoreCase("abc", null) = false
- * StringUtils.equalsIgnoreCase("abc", "abc") = true
- * StringUtils.equalsIgnoreCase("abc", "ABC") = true
- * </pre>
- *
- * @param str1 the first CharSequence, may be null
- * @param str2 the second CharSequence, may be null
- * @return {@code true} if the CharSequence are equal, case insensitive, or
- * both {@code null}
- * @since 3.0 Changed signature from equalsIgnoreCase(String, String) to equalsIgnoreCase(CharSequence, CharSequence)
- */
- public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) {
- if (str1 == null || str2 == null) {
- return str1 == str2;
- } else {
- return CharSequenceUtils.regionMatches(str1, true, 0, str2, 0, Math.max(str1.length(), str2.length()));
- }
- }
-
- // IndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the first index within a CharSequence, handling {@code null}.
- * This method uses {@link String#indexOf(int, int)} if possible.</p>
- *
- * <p>A {@code null} or empty ("") CharSequence will return {@code INDEX_NOT_FOUND (-1)}.</p>
- *
- * <pre>
- * StringUtils.indexOf(null, *) = -1
- * StringUtils.indexOf("", *) = -1
- * StringUtils.indexOf("aabaabaa", 'a') = 0
- * StringUtils.indexOf("aabaabaa", 'b') = 2
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChar the character to find
- * @return the first index of the search character,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from indexOf(String, int) to indexOf(CharSequence, int)
- */
- public static int indexOf(CharSequence seq, int searchChar) {
- if (isEmpty(seq)) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.indexOf(seq, searchChar, 0);
- }
-
- /**
- * <p>Finds the first index within a CharSequence from a start position,
- * handling {@code null}.
- * This method uses {@link String#indexOf(int, int)} if possible.</p>
- *
- * <p>A {@code null} or empty ("") CharSequence will return {@code (INDEX_NOT_FOUND) -1}.
- * A negative start position is treated as zero.
- * A start position greater than the string length returns {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOf(null, *, *) = -1
- * StringUtils.indexOf("", *, *) = -1
- * StringUtils.indexOf("aabaabaa", 'b', 0) = 2
- * StringUtils.indexOf("aabaabaa", 'b', 3) = 5
- * StringUtils.indexOf("aabaabaa", 'b', 9) = -1
- * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChar the character to find
- * @param startPos the start position, negative treated as zero
- * @return the first index of the search character,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from indexOf(String, int, int) to indexOf(CharSequence, int, int)
- */
- public static int indexOf(CharSequence seq, int searchChar, int startPos) {
- if (isEmpty(seq)) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.indexOf(seq, searchChar, startPos);
- }
-
- /**
- * <p>Finds the first index within a CharSequence, handling {@code null}.
- * This method uses {@link String#indexOf(String, int)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOf(null, *) = -1
- * StringUtils.indexOf(*, null) = -1
- * StringUtils.indexOf("", "") = 0
- * StringUtils.indexOf("", *) = -1 (except when * = "")
- * StringUtils.indexOf("aabaabaa", "a") = 0
- * StringUtils.indexOf("aabaabaa", "b") = 2
- * StringUtils.indexOf("aabaabaa", "ab") = 1
- * StringUtils.indexOf("aabaabaa", "") = 0
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchSeq the CharSequence to find, may be null
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from indexOf(String, String) to indexOf(CharSequence, CharSequence)
- */
- public static int indexOf(CharSequence seq, CharSequence searchSeq) {
- if (seq == null || searchSeq == null) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.indexOf(seq, searchSeq, 0);
- }
-
- /**
- * <p>Finds the first index within a CharSequence, handling {@code null}.
- * This method uses {@link String#indexOf(String, int)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position is treated as zero.
- * An empty ("") search CharSequence always matches.
- * A start position greater than the string length only matches
- * an empty search CharSequence.</p>
- *
- * <pre>
- * StringUtils.indexOf(null, *, *) = -1
- * StringUtils.indexOf(*, null, *) = -1
- * StringUtils.indexOf("", "", 0) = 0
- * StringUtils.indexOf("", *, 0) = -1 (except when * = "")
- * StringUtils.indexOf("aabaabaa", "a", 0) = 0
- * StringUtils.indexOf("aabaabaa", "b", 0) = 2
- * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
- * StringUtils.indexOf("aabaabaa", "b", 3) = 5
- * StringUtils.indexOf("aabaabaa", "b", 9) = -1
- * StringUtils.indexOf("aabaabaa", "b", -1) = 2
- * StringUtils.indexOf("aabaabaa", "", 2) = 2
- * StringUtils.indexOf("abc", "", 9) = 3
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchSeq the CharSequence to find, may be null
- * @param startPos the start position, negative treated as zero
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from indexOf(String, String, int) to indexOf(CharSequence, CharSequence, int)
- */
- public static int indexOf(CharSequence seq, CharSequence searchSeq, int startPos) {
- if (seq == null || searchSeq == null) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.indexOf(seq, searchSeq, startPos);
- }
-
- /**
- * <p>Finds the n-th index within a CharSequence, handling {@code null}.
- * This method uses {@link String#indexOf(String)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.ordinalIndexOf(null, *, *) = -1
- * StringUtils.ordinalIndexOf(*, null, *) = -1
- * StringUtils.ordinalIndexOf("", "", *) = 0
- * StringUtils.ordinalIndexOf("aabaabaa", "a", 1) = 0
- * StringUtils.ordinalIndexOf("aabaabaa", "a", 2) = 1
- * StringUtils.ordinalIndexOf("aabaabaa", "b", 1) = 2
- * StringUtils.ordinalIndexOf("aabaabaa", "b", 2) = 5
- * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
- * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
- * StringUtils.ordinalIndexOf("aabaabaa", "", 1) = 0
- * StringUtils.ordinalIndexOf("aabaabaa", "", 2) = 0
- * </pre>
- *
- * <p>Note that 'head(CharSequence str, int n)' may be implemented as: </p>
- *
- * <pre>
- * str.substring(0, lastOrdinalIndexOf(str, "\n", n))
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @param ordinal the n-th {@code searchStr} to find
- * @return the n-th index of the search CharSequence,
- * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
- * @since 2.1
- * @since 3.0 Changed signature from ordinalIndexOf(String, String, int) to ordinalIndexOf(CharSequence, CharSequence, int)
- */
- public static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) {
- return ordinalIndexOf(str, searchStr, ordinal, false);
- }
-
- /**
- * <p>Finds the n-th index within a String, handling {@code null}.
- * This method uses {@link String#indexOf(String)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.</p>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @param ordinal the n-th {@code searchStr} to find
- * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf()
- * @return the n-th index of the search CharSequence,
- * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
- */
- // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int)
- private static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal, boolean lastIndex) {
- if (str == null || searchStr == null || ordinal <= 0) {
- return INDEX_NOT_FOUND;
- }
- if (searchStr.length() == 0) {
- return lastIndex ? str.length() : 0;
- }
- int found = 0;
- int index = lastIndex ? str.length() : INDEX_NOT_FOUND;
- do {
- if (lastIndex) {
- index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1);
- } else {
- index = CharSequenceUtils.indexOf(str, searchStr, index + 1);
- }
- if (index < 0) {
- return index;
- }
- found++;
- } while (found < ordinal);
- return index;
- }
-
- /**
- * <p>Case in-sensitive find of the first index within a CharSequence.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position is treated as zero.
- * An empty ("") search CharSequence always matches.
- * A start position greater than the string length only matches
- * an empty search CharSequence.</p>
- *
- * <pre>
- * StringUtils.indexOfIgnoreCase(null, *) = -1
- * StringUtils.indexOfIgnoreCase(*, null) = -1
- * StringUtils.indexOfIgnoreCase("", "") = 0
- * StringUtils.indexOfIgnoreCase("aabaabaa", "a") = 0
- * StringUtils.indexOfIgnoreCase("aabaabaa", "b") = 2
- * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.5
- * @since 3.0 Changed signature from indexOfIgnoreCase(String, String) to indexOfIgnoreCase(CharSequence, CharSequence)
- */
- public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
- return indexOfIgnoreCase(str, searchStr, 0);
- }
-
- /**
- * <p>Case in-sensitive find of the first index within a CharSequence
- * from the specified position.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position is treated as zero.
- * An empty ("") search CharSequence always matches.
- * A start position greater than the string length only matches
- * an empty search CharSequence.</p>
- *
- * <pre>
- * StringUtils.indexOfIgnoreCase(null, *, *) = -1
- * StringUtils.indexOfIgnoreCase(*, null, *) = -1
- * StringUtils.indexOfIgnoreCase("", "", 0) = 0
- * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0) = 0
- * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0) = 2
- * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
- * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3) = 5
- * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9) = -1
- * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
- * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2) = 2
- * StringUtils.indexOfIgnoreCase("abc", "", 9) = 3
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @param startPos the start position, negative treated as zero
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.5
- * @since 3.0 Changed signature from indexOfIgnoreCase(String, String, int) to indexOfIgnoreCase(CharSequence, CharSequence, int)
- */
- public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) {
- if (str == null || searchStr == null) {
- return INDEX_NOT_FOUND;
- }
- if (startPos < 0) {
- startPos = 0;
- }
- int endLimit = (str.length() - searchStr.length()) + 1;
- if (startPos > endLimit) {
- return INDEX_NOT_FOUND;
- }
- if (searchStr.length() == 0) {
- return startPos;
- }
- for (int i = startPos; i < endLimit; i++) {
- if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- // LastIndexOf
- //-----------------------------------------------------------------------
- /**
- * <p>Finds the last index within a CharSequence, handling {@code null}.
- * This method uses {@link String#lastIndexOf(int)} if possible.</p>
- *
- * <p>A {@code null} or empty ("") CharSequence will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.lastIndexOf(null, *) = -1
- * StringUtils.lastIndexOf("", *) = -1
- * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
- * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChar the character to find
- * @return the last index of the search character,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from lastIndexOf(String, int) to lastIndexOf(CharSequence, int)
- */
- public static int lastIndexOf(CharSequence seq, int searchChar) {
- if (isEmpty(seq)) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length());
- }
-
- /**
- * <p>Finds the last index within a CharSequence from a start position,
- * handling {@code null}.
- * This method uses {@link String#lastIndexOf(int, int)} if possible.</p>
- *
- * <p>A {@code null} or empty ("") CharSequence will return {@code -1}.
- * A negative start position returns {@code -1}.
- * A start position greater than the string length searches the whole string.</p>
- *
- * <pre>
- * StringUtils.lastIndexOf(null, *, *) = -1
- * StringUtils.lastIndexOf("", *, *) = -1
- * StringUtils.lastIndexOf("aabaabaa", 'b', 8) = 5
- * StringUtils.lastIndexOf("aabaabaa", 'b', 4) = 2
- * StringUtils.lastIndexOf("aabaabaa", 'b', 0) = -1
- * StringUtils.lastIndexOf("aabaabaa", 'b', 9) = 5
- * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
- * StringUtils.lastIndexOf("aabaabaa", 'a', 0) = 0
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChar the character to find
- * @param startPos the start position
- * @return the last index of the search character,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from lastIndexOf(String, int, int) to lastIndexOf(CharSequence, int, int)
- */
- public static int lastIndexOf(CharSequence seq, int searchChar, int startPos) {
- if (isEmpty(seq)) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos);
- }
-
- /**
- * <p>Finds the last index within a CharSequence, handling {@code null}.
- * This method uses {@link String#lastIndexOf(String)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.lastIndexOf(null, *) = -1
- * StringUtils.lastIndexOf(*, null) = -1
- * StringUtils.lastIndexOf("", "") = 0
- * StringUtils.lastIndexOf("aabaabaa", "a") = 7
- * StringUtils.lastIndexOf("aabaabaa", "b") = 5
- * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
- * StringUtils.lastIndexOf("aabaabaa", "") = 8
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchSeq the CharSequence to find, may be null
- * @return the last index of the search String,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from lastIndexOf(String, String) to lastIndexOf(CharSequence, CharSequence)
- */
- public static int lastIndexOf(CharSequence seq, CharSequence searchSeq) {
- if (seq == null || searchSeq == null) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.lastIndexOf(seq, searchSeq, seq.length());
- }
-
- /**
- * <p>Finds the n-th last index within a String, handling {@code null}.
- * This method uses {@link String#lastIndexOf(String)}.</p>
- *
- * <p>A {@code null} String will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.lastOrdinalIndexOf(null, *, *) = -1
- * StringUtils.lastOrdinalIndexOf(*, null, *) = -1
- * StringUtils.lastOrdinalIndexOf("", "", *) = 0
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1) = 7
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2) = 6
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1) = 5
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2) = 2
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1) = 8
- * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2) = 8
- * </pre>
- *
- * <p>Note that 'tail(CharSequence str, int n)' may be implemented as: </p>
- *
- * <pre>
- * str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @param ordinal the n-th last {@code searchStr} to find
- * @return the n-th last index of the search CharSequence,
- * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input
- * @since 2.5
- * @since 3.0 Changed signature from lastOrdinalIndexOf(String, String, int) to lastOrdinalIndexOf(CharSequence, CharSequence, int)
- */
- public static int lastOrdinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) {
- return ordinalIndexOf(str, searchStr, ordinal, true);
- }
-
- /**
- * <p>Finds the first index within a CharSequence, handling {@code null}.
- * This method uses {@link String#lastIndexOf(String, int)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position returns {@code -1}.
- * An empty ("") search CharSequence always matches unless the start position is negative.
- * A start position greater than the string length searches the whole string.</p>
- *
- * <pre>
- * StringUtils.lastIndexOf(null, *, *) = -1
- * StringUtils.lastIndexOf(*, null, *) = -1
- * StringUtils.lastIndexOf("aabaabaa", "a", 8) = 7
- * StringUtils.lastIndexOf("aabaabaa", "b", 8) = 5
- * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4
- * StringUtils.lastIndexOf("aabaabaa", "b", 9) = 5
- * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1
- * StringUtils.lastIndexOf("aabaabaa", "a", 0) = 0
- * StringUtils.lastIndexOf("aabaabaa", "b", 0) = -1
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchSeq the CharSequence to find, may be null
- * @param startPos the start position, negative treated as zero
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from lastIndexOf(String, String, int) to lastIndexOf(CharSequence, CharSequence, int)
- */
- public static int lastIndexOf(CharSequence seq, CharSequence searchSeq, int startPos) {
- if (seq == null || searchSeq == null) {
- return INDEX_NOT_FOUND;
- }
- return CharSequenceUtils.lastIndexOf(seq, searchSeq, startPos);
- }
-
- /**
- * <p>Case in-sensitive find of the last index within a CharSequence.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position returns {@code -1}.
- * An empty ("") search CharSequence always matches unless the start position is negative.
- * A start position greater than the string length searches the whole string.</p>
- *
- * <pre>
- * StringUtils.lastIndexOfIgnoreCase(null, *) = -1
- * StringUtils.lastIndexOfIgnoreCase(*, null) = -1
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A") = 7
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B") = 5
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} string input
- * @since 2.5
- * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String) to lastIndexOfIgnoreCase(CharSequence, CharSequence)
- */
- public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) {
- if (str == null || searchStr == null) {
- return INDEX_NOT_FOUND;
- }
- return lastIndexOfIgnoreCase(str, searchStr, str.length());
- }
-
- /**
- * <p>Case in-sensitive find of the last index within a CharSequence
- * from the specified position.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A negative start position returns {@code -1}.
- * An empty ("") search CharSequence always matches unless the start position is negative.
- * A start position greater than the string length searches the whole string.</p>
- *
- * <pre>
- * StringUtils.lastIndexOfIgnoreCase(null, *, *) = -1
- * StringUtils.lastIndexOfIgnoreCase(*, null, *) = -1
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8) = 7
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8) = 5
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9) = 5
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0) = 0
- * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0) = -1
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @param startPos the start position
- * @return the first index of the search CharSequence,
- * -1 if no match or {@code null} input
- * @since 2.5
- * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String, int) to lastIndexOfIgnoreCase(CharSequence, CharSequence, int)
- */
- public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) {
- if (str == null || searchStr == null) {
- return INDEX_NOT_FOUND;
- }
- if (startPos > (str.length() - searchStr.length())) {
- startPos = str.length() - searchStr.length();
- }
- if (startPos < 0) {
- return INDEX_NOT_FOUND;
- }
- if (searchStr.length() == 0) {
- return startPos;
- }
-
- for (int i = startPos; i >= 0; i--) {
- if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) {
- return i;
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- // Contains
- //-----------------------------------------------------------------------
- /**
- * <p>Checks if CharSequence contains a search character, handling {@code null}.
- * This method uses {@link String#indexOf(int)} if possible.</p>
- *
- * <p>A {@code null} or empty ("") CharSequence will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.contains(null, *) = false
- * StringUtils.contains("", *) = false
- * StringUtils.contains("abc", 'a') = true
- * StringUtils.contains("abc", 'z') = false
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChar the character to find
- * @return true if the CharSequence contains the search character,
- * false if not or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from contains(String, int) to contains(CharSequence, int)
- */
- public static boolean contains(CharSequence seq, int searchChar) {
- if (isEmpty(seq)) {
- return false;
- }
- return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0;
- }
-
- /**
- * <p>Checks if CharSequence contains a search CharSequence, handling {@code null}.
- * This method uses {@link String#indexOf(String)} if possible.</p>
- *
- * <p>A {@code null} CharSequence will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.contains(null, *) = false
- * StringUtils.contains(*, null) = false
- * StringUtils.contains("", "") = true
- * StringUtils.contains("abc", "") = true
- * StringUtils.contains("abc", "a") = true
- * StringUtils.contains("abc", "z") = false
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchSeq the CharSequence to find, may be null
- * @return true if the CharSequence contains the search CharSequence,
- * false if not or {@code null} string input
- * @since 2.0
- * @since 3.0 Changed signature from contains(String, String) to contains(CharSequence, CharSequence)
- */
- public static boolean contains(CharSequence seq, CharSequence searchSeq) {
- if (seq == null || searchSeq == null) {
- return false;
- }
- return CharSequenceUtils.indexOf(seq, searchSeq, 0) >= 0;
- }
-
- /**
- * <p>Checks if CharSequence contains a search CharSequence irrespective of case,
- * handling {@code null}. Case-insensitivity is defined as by
- * {@link String#equalsIgnoreCase(String)}.
- *
- * <p>A {@code null} CharSequence will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.contains(null, *) = false
- * StringUtils.contains(*, null) = false
- * StringUtils.contains("", "") = true
- * StringUtils.contains("abc", "") = true
- * StringUtils.contains("abc", "a") = true
- * StringUtils.contains("abc", "z") = false
- * StringUtils.contains("abc", "A") = true
- * StringUtils.contains("abc", "Z") = false
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStr the CharSequence to find, may be null
- * @return true if the CharSequence contains the search CharSequence irrespective of
- * case or false if not or {@code null} string input
- * @since 3.0 Changed signature from containsIgnoreCase(String, String) to containsIgnoreCase(CharSequence, CharSequence)
- */
- public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) {
- if (str == null || searchStr == null) {
- return false;
- }
- int len = searchStr.length();
- int max = str.length() - len;
- for (int i = 0; i <= max; i++) {
- if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * Check whether the given CharSequence contains any whitespace characters.
- * @param seq the CharSequence to check (may be {@code null})
- * @return {@code true} if the CharSequence is not empty and
- * contains at least 1 whitespace character
- * @see java.lang.Character#isWhitespace
- * @since 3.0
- */
- // From org.springframework.util.StringUtils, under Apache License 2.0
- public static boolean containsWhitespace(CharSequence seq) {
- if (isEmpty(seq)) {
- return false;
- }
- int strLen = seq.length();
- for (int i = 0; i < strLen; i++) {
- if (Character.isWhitespace(seq.charAt(i))) {
- return true;
- }
- }
- return false;
- }
-
- // IndexOfAny chars
- //-----------------------------------------------------------------------
- /**
- * <p>Search a CharSequence to find the first index of any
- * character in the given set of characters.</p>
- *
- * <p>A {@code null} String will return {@code -1}.
- * A {@code null} or zero length search array will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOfAny(null, *) = -1
- * StringUtils.indexOfAny("", *) = -1
- * StringUtils.indexOfAny(*, null) = -1
- * StringUtils.indexOfAny(*, []) = -1
- * StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0
- * StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3
- * StringUtils.indexOfAny("aba", ['z']) = -1
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param searchChars the chars to search for, may be null
- * @return the index of any of the chars, -1 if no match or null input
- * @since 2.0
- * @since 3.0 Changed signature from indexOfAny(String, char[]) to indexOfAny(CharSequence, char...)
- */
- public static int indexOfAny(CharSequence cs, char... searchChars) {
- if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
- return INDEX_NOT_FOUND;
- }
- int csLen = cs.length();
- int csLast = csLen - 1;
- int searchLen = searchChars.length;
- int searchLast = searchLen - 1;
- for (int i = 0; i < csLen; i++) {
- char ch = cs.charAt(i);
- for (int j = 0; j < searchLen; j++) {
- if (searchChars[j] == ch) {
- if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) {
- // ch is a supplementary character
- if (searchChars[j + 1] == cs.charAt(i + 1)) {
- return i;
- }
- } else {
- return i;
- }
- }
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Search a CharSequence to find the first index of any
- * character in the given set of characters.</p>
- *
- * <p>A {@code null} String will return {@code -1}.
- * A {@code null} search string will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOfAny(null, *) = -1
- * StringUtils.indexOfAny("", *) = -1
- * StringUtils.indexOfAny(*, null) = -1
- * StringUtils.indexOfAny(*, "") = -1
- * StringUtils.indexOfAny("zzabyycdxx", "za") = 0
- * StringUtils.indexOfAny("zzabyycdxx", "by") = 3
- * StringUtils.indexOfAny("aba","z") = -1
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param searchChars the chars to search for, may be null
- * @return the index of any of the chars, -1 if no match or null input
- * @since 2.0
- * @since 3.0 Changed signature from indexOfAny(String, String) to indexOfAny(CharSequence, String)
- */
- public static int indexOfAny(CharSequence cs, String searchChars) {
- if (isEmpty(cs) || isEmpty(searchChars)) {
- return INDEX_NOT_FOUND;
- }
- return indexOfAny(cs, searchChars.toCharArray());
- }
-
- // ContainsAny
- //-----------------------------------------------------------------------
- /**
- * <p>Checks if the CharSequence contains any character in the given
- * set of characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code false}.
- * A {@code null} or zero length search array will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.containsAny(null, *) = false
- * StringUtils.containsAny("", *) = false
- * StringUtils.containsAny(*, null) = false
- * StringUtils.containsAny(*, []) = false
- * StringUtils.containsAny("zzabyycdxx",['z','a']) = true
- * StringUtils.containsAny("zzabyycdxx",['b','y']) = true
- * StringUtils.containsAny("aba", ['z']) = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param searchChars the chars to search for, may be null
- * @return the {@code true} if any of the chars are found,
- * {@code false} if no match or null input
- * @since 2.4
- * @since 3.0 Changed signature from containsAny(String, char[]) to containsAny(CharSequence, char...)
- */
- public static boolean containsAny(CharSequence cs, char... searchChars) {
- if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
- return false;
- }
- int csLength = cs.length();
- int searchLength = searchChars.length;
- int csLast = csLength - 1;
- int searchLast = searchLength - 1;
- for (int i = 0; i < csLength; i++) {
- char ch = cs.charAt(i);
- for (int j = 0; j < searchLength; j++) {
- if (searchChars[j] == ch) {
- if (Character.isHighSurrogate(ch)) {
- if (j == searchLast) {
- // missing low surrogate, fine, like String.indexOf(String)
- return true;
- }
- if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
- return true;
- }
- } else {
- // ch is in the Basic Multilingual Plane
- return true;
- }
- }
- }
- }
- return false;
- }
-
- /**
- * <p>
- * Checks if the CharSequence contains any character in the given set of characters.
- * </p>
- *
- * <p>
- * A {@code null} CharSequence will return {@code false}. A {@code null} search CharSequence will return
- * {@code false}.
- * </p>
- *
- * <pre>
- * StringUtils.containsAny(null, *) = false
- * StringUtils.containsAny("", *) = false
- * StringUtils.containsAny(*, null) = false
- * StringUtils.containsAny(*, "") = false
- * StringUtils.containsAny("zzabyycdxx", "za") = true
- * StringUtils.containsAny("zzabyycdxx", "by") = true
- * StringUtils.containsAny("aba","z") = false
- * </pre>
- *
- * @param cs
- * the CharSequence to check, may be null
- * @param searchChars
- * the chars to search for, may be null
- * @return the {@code true} if any of the chars are found, {@code false} if no match or null input
- * @since 2.4
- * @since 3.0 Changed signature from containsAny(String, String) to containsAny(CharSequence, CharSequence)
- */
- public static boolean containsAny(CharSequence cs, CharSequence searchChars) {
- if (searchChars == null) {
- return false;
- }
- return containsAny(cs, CharSequenceUtils.toCharArray(searchChars));
- }
-
- // IndexOfAnyBut chars
- //-----------------------------------------------------------------------
- /**
- * <p>Searches a CharSequence to find the first index of any
- * character not in the given set of characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A {@code null} or zero length search array will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOfAnyBut(null, *) = -1
- * StringUtils.indexOfAnyBut("", *) = -1
- * StringUtils.indexOfAnyBut(*, null) = -1
- * StringUtils.indexOfAnyBut(*, []) = -1
- * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
- * StringUtils.indexOfAnyBut("aba", new char[] {'z'} ) = 0
- * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} ) = -1
-
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param searchChars the chars to search for, may be null
- * @return the index of any of the chars, -1 if no match or null input
- * @since 2.0
- * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to indexOfAnyBut(CharSequence, char...)
- */
- public static int indexOfAnyBut(CharSequence cs, char... searchChars) {
- if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) {
- return INDEX_NOT_FOUND;
- }
- int csLen = cs.length();
- int csLast = csLen - 1;
- int searchLen = searchChars.length;
- int searchLast = searchLen - 1;
- outer:
- for (int i = 0; i < csLen; i++) {
- char ch = cs.charAt(i);
- for (int j = 0; j < searchLen; j++) {
- if (searchChars[j] == ch) {
- if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) {
- if (searchChars[j + 1] == cs.charAt(i + 1)) {
- continue outer;
- }
- } else {
- continue outer;
- }
- }
- }
- return i;
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Search a CharSequence to find the first index of any
- * character not in the given set of characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A {@code null} or empty search string will return {@code -1}.</p>
- *
- * <pre>
- * StringUtils.indexOfAnyBut(null, *) = -1
- * StringUtils.indexOfAnyBut("", *) = -1
- * StringUtils.indexOfAnyBut(*, null) = -1
- * StringUtils.indexOfAnyBut(*, "") = -1
- * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3
- * StringUtils.indexOfAnyBut("zzabyycdxx", "") = -1
- * StringUtils.indexOfAnyBut("aba","ab") = -1
- * </pre>
- *
- * @param seq the CharSequence to check, may be null
- * @param searchChars the chars to search for, may be null
- * @return the index of any of the chars, -1 if no match or null input
- * @since 2.0
- * @since 3.0 Changed signature from indexOfAnyBut(String, String) to indexOfAnyBut(CharSequence, CharSequence)
- */
- public static int indexOfAnyBut(CharSequence seq, CharSequence searchChars) {
- if (isEmpty(seq) || isEmpty(searchChars)) {
- return INDEX_NOT_FOUND;
- }
- int strLen = seq.length();
- for (int i = 0; i < strLen; i++) {
- char ch = seq.charAt(i);
- boolean chFound = CharSequenceUtils.indexOf(searchChars, ch, 0) >= 0;
- if (i + 1 < strLen && Character.isHighSurrogate(ch)) {
- char ch2 = seq.charAt(i + 1);
- if (chFound && CharSequenceUtils.indexOf(searchChars, ch2, 0) < 0) {
- return i;
- }
- } else {
- if (!chFound) {
- return i;
- }
- }
- }
- return INDEX_NOT_FOUND;
- }
-
- // ContainsOnly
- //-----------------------------------------------------------------------
- /**
- * <p>Checks if the CharSequence contains only certain characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code false}.
- * A {@code null} valid character array will return {@code false}.
- * An empty CharSequence (length()=0) always returns {@code true}.</p>
- *
- * <pre>
- * StringUtils.containsOnly(null, *) = false
- * StringUtils.containsOnly(*, null) = false
- * StringUtils.containsOnly("", *) = true
- * StringUtils.containsOnly("ab", '') = false
- * StringUtils.containsOnly("abab", 'abc') = true
- * StringUtils.containsOnly("ab1", 'abc') = false
- * StringUtils.containsOnly("abz", 'abc') = false
- * </pre>
- *
- * @param cs the String to check, may be null
- * @param valid an array of valid chars, may be null
- * @return true if it only contains valid chars and is non-null
- * @since 3.0 Changed signature from containsOnly(String, char[]) to containsOnly(CharSequence, char...)
- */
- public static boolean containsOnly(CharSequence cs, char... valid) {
- // All these pre-checks are to maintain API with an older version
- if (valid == null || cs == null) {
- return false;
- }
- if (cs.length() == 0) {
- return true;
- }
- if (valid.length == 0) {
- return false;
- }
- return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Checks if the CharSequence contains only certain characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code false}.
- * A {@code null} valid character String will return {@code false}.
- * An empty String (length()=0) always returns {@code true}.</p>
- *
- * <pre>
- * StringUtils.containsOnly(null, *) = false
- * StringUtils.containsOnly(*, null) = false
- * StringUtils.containsOnly("", *) = true
- * StringUtils.containsOnly("ab", "") = false
- * StringUtils.containsOnly("abab", "abc") = true
- * StringUtils.containsOnly("ab1", "abc") = false
- * StringUtils.containsOnly("abz", "abc") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param validChars a String of valid chars, may be null
- * @return true if it only contains valid chars and is non-null
- * @since 2.0
- * @since 3.0 Changed signature from containsOnly(String, String) to containsOnly(CharSequence, String)
- */
- public static boolean containsOnly(CharSequence cs, String validChars) {
- if (cs == null || validChars == null) {
- return false;
- }
- return containsOnly(cs, validChars.toCharArray());
- }
-
- // ContainsNone
- //-----------------------------------------------------------------------
- /**
- * <p>Checks that the CharSequence does not contain certain characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code true}.
- * A {@code null} invalid character array will return {@code true}.
- * An empty CharSequence (length()=0) always returns true.</p>
- *
- * <pre>
- * StringUtils.containsNone(null, *) = true
- * StringUtils.containsNone(*, null) = true
- * StringUtils.containsNone("", *) = true
- * StringUtils.containsNone("ab", '') = true
- * StringUtils.containsNone("abab", 'xyz') = true
- * StringUtils.containsNone("ab1", 'xyz') = true
- * StringUtils.containsNone("abz", 'xyz') = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param searchChars an array of invalid chars, may be null
- * @return true if it contains none of the invalid chars, or is null
- * @since 2.0
- * @since 3.0 Changed signature from containsNone(String, char[]) to containsNone(CharSequence, char...)
- */
- public static boolean containsNone(CharSequence cs, char... searchChars) {
- if (cs == null || searchChars == null) {
- return true;
- }
- int csLen = cs.length();
- int csLast = csLen - 1;
- int searchLen = searchChars.length;
- int searchLast = searchLen - 1;
- for (int i = 0; i < csLen; i++) {
- char ch = cs.charAt(i);
- for (int j = 0; j < searchLen; j++) {
- if (searchChars[j] == ch) {
- if (Character.isHighSurrogate(ch)) {
- if (j == searchLast) {
- // missing low surrogate, fine, like String.indexOf(String)
- return false;
- }
- if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) {
- return false;
- }
- } else {
- // ch is in the Basic Multilingual Plane
- return false;
- }
- }
- }
- }
- return true;
- }
-
- /**
- * <p>Checks that the CharSequence does not contain certain characters.</p>
- *
- * <p>A {@code null} CharSequence will return {@code true}.
- * A {@code null} invalid character array will return {@code true}.
- * An empty String ("") always returns true.</p>
- *
- * <pre>
- * StringUtils.containsNone(null, *) = true
- * StringUtils.containsNone(*, null) = true
- * StringUtils.containsNone("", *) = true
- * StringUtils.containsNone("ab", "") = true
- * StringUtils.containsNone("abab", "xyz") = true
- * StringUtils.containsNone("ab1", "xyz") = true
- * StringUtils.containsNone("abz", "xyz") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @param invalidChars a String of invalid chars, may be null
- * @return true if it contains none of the invalid chars, or is null
- * @since 2.0
- * @since 3.0 Changed signature from containsNone(String, String) to containsNone(CharSequence, String)
- */
- public static boolean containsNone(CharSequence cs, String invalidChars) {
- if (cs == null || invalidChars == null) {
- return true;
- }
- return containsNone(cs, invalidChars.toCharArray());
- }
-
- // IndexOfAny strings
- //-----------------------------------------------------------------------
- /**
- * <p>Find the first index of any of a set of potential substrings.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A {@code null} or zero length search array will return {@code -1}.
- * A {@code null} search array entry will be ignored, but a search
- * array containing "" will return {@code 0} if {@code str} is not
- * null. This method uses {@link String#indexOf(String)} if possible.</p>
- *
- * <pre>
- * StringUtils.indexOfAny(null, *) = -1
- * StringUtils.indexOfAny(*, null) = -1
- * StringUtils.indexOfAny(*, []) = -1
- * StringUtils.indexOfAny("zzabyycdxx", ["ab","cd"]) = 2
- * StringUtils.indexOfAny("zzabyycdxx", ["cd","ab"]) = 2
- * StringUtils.indexOfAny("zzabyycdxx", ["mn","op"]) = -1
- * StringUtils.indexOfAny("zzabyycdxx", ["zab","aby"]) = 1
- * StringUtils.indexOfAny("zzabyycdxx", [""]) = 0
- * StringUtils.indexOfAny("", [""]) = 0
- * StringUtils.indexOfAny("", ["a"]) = -1
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStrs the CharSequences to search for, may be null
- * @return the first index of any of the searchStrs in str, -1 if no match
- * @since 3.0 Changed signature from indexOfAny(String, String[]) to indexOfAny(CharSequence, CharSequence...)
- */
- public static int indexOfAny(CharSequence str, CharSequence... searchStrs) {
- if (str == null || searchStrs == null) {
- return INDEX_NOT_FOUND;
- }
- int sz = searchStrs.length;
-
- // String's can't have a MAX_VALUEth index.
- int ret = Integer.MAX_VALUE;
-
- int tmp = 0;
- for (int i = 0; i < sz; i++) {
- CharSequence search = searchStrs[i];
- if (search == null) {
- continue;
- }
- tmp = CharSequenceUtils.indexOf(str, search, 0);
- if (tmp == INDEX_NOT_FOUND) {
- continue;
- }
-
- if (tmp < ret) {
- ret = tmp;
- }
- }
-
- return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret;
- }
-
- /**
- * <p>Find the latest index of any of a set of potential substrings.</p>
- *
- * <p>A {@code null} CharSequence will return {@code -1}.
- * A {@code null} search array will return {@code -1}.
- * A {@code null} or zero length search array entry will be ignored,
- * but a search array containing "" will return the length of {@code str}
- * if {@code str} is not null. This method uses {@link String#indexOf(String)} if possible</p>
- *
- * <pre>
- * StringUtils.lastIndexOfAny(null, *) = -1
- * StringUtils.lastIndexOfAny(*, null) = -1
- * StringUtils.lastIndexOfAny(*, []) = -1
- * StringUtils.lastIndexOfAny(*, [null]) = -1
- * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6
- * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6
- * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
- * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
- * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""]) = 10
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param searchStrs the CharSequences to search for, may be null
- * @return the last index of any of the CharSequences, -1 if no match
- * @since 3.0 Changed signature from lastIndexOfAny(String, String[]) to lastIndexOfAny(CharSequence, CharSequence)
- */
- public static int lastIndexOfAny(CharSequence str, CharSequence... searchStrs) {
- if (str == null || searchStrs == null) {
- return INDEX_NOT_FOUND;
- }
- int sz = searchStrs.length;
- int ret = INDEX_NOT_FOUND;
- int tmp = 0;
- for (int i = 0; i < sz; i++) {
- CharSequence search = searchStrs[i];
- if (search == null) {
- continue;
- }
- tmp = CharSequenceUtils.lastIndexOf(str, search, str.length());
- if (tmp > ret) {
- ret = tmp;
- }
- }
- return ret;
- }
-
- // Substring
- //-----------------------------------------------------------------------
- /**
- * <p>Gets a substring from the specified String avoiding exceptions.</p>
- *
- * <p>A negative start position can be used to start {@code n}
- * characters from the end of the String.</p>
- *
- * <p>A {@code null} String will return {@code null}.
- * An empty ("") String will return "".</p>
- *
- * <pre>
- * StringUtils.substring(null, *) = null
- * StringUtils.substring("", *) = ""
- * StringUtils.substring("abc", 0) = "abc"
- * StringUtils.substring("abc", 2) = "c"
- * StringUtils.substring("abc", 4) = ""
- * StringUtils.substring("abc", -2) = "bc"
- * StringUtils.substring("abc", -4) = "abc"
- * </pre>
- *
- * @param str the String to get the substring from, may be null
- * @param start the position to start from, negative means
- * count back from the end of the String by this many characters
- * @return substring from start position, {@code null} if null String input
- */
- public static String substring(String str, int start) {
- if (str == null) {
- return null;
- }
-
- // handle negatives, which means last n characters
- if (start < 0) {
- start = str.length() + start; // remember start is negative
- }
-
- if (start < 0) {
- start = 0;
- }
- if (start > str.length()) {
- return EMPTY;
- }
-
- return str.substring(start);
- }
-
- /**
- * <p>Gets a substring from the specified String avoiding exceptions.</p>
- *
- * <p>A negative start position can be used to start/end {@code n}
- * characters from the end of the String.</p>
- *
- * <p>The returned substring starts with the character in the {@code start}
- * position and ends before the {@code end} position. All position counting is
- * zero-based -- i.e., to start at the beginning of the string use
- * {@code start = 0}. Negative start and end positions can be used to
- * specify offsets relative to the end of the String.</p>
- *
- * <p>If {@code start} is not strictly to the left of {@code end}, ""
- * is returned.</p>
- *
- * <pre>
- * StringUtils.substring(null, *, *) = null
- * StringUtils.substring("", * , *) = "";
- * StringUtils.substring("abc", 0, 2) = "ab"
- * StringUtils.substring("abc", 2, 0) = ""
- * StringUtils.substring("abc", 2, 4) = "c"
- * StringUtils.substring("abc", 4, 6) = ""
- * StringUtils.substring("abc", 2, 2) = ""
- * StringUtils.substring("abc", -2, -1) = "b"
- * StringUtils.substring("abc", -4, 2) = "ab"
- * </pre>
- *
- * @param str the String to get the substring from, may be null
- * @param start the position to start from, negative means
- * count back from the end of the String by this many characters
- * @param end the position to end at (exclusive), negative means
- * count back from the end of the String by this many characters
- * @return substring from start position to end position,
- * {@code null} if null String input
- */
- public static String substring(String str, int start, int end) {
- if (str == null) {
- return null;
- }
-
- // handle negatives
- if (end < 0) {
- end = str.length() + end; // remember end is negative
- }
- if (start < 0) {
- start = str.length() + start; // remember start is negative
- }
-
- // check length next
- if (end > str.length()) {
- end = str.length();
- }
-
- // if start is greater than end, return ""
- if (start > end) {
- return EMPTY;
- }
-
- if (start < 0) {
- start = 0;
- }
- if (end < 0) {
- end = 0;
- }
-
- return str.substring(start, end);
- }
-
- // Left/Right/Mid
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the leftmost {@code len} characters of a String.</p>
- *
- * <p>If {@code len} characters are not available, or the
- * String is {@code null}, the String will be returned without
- * an exception. An empty String is returned if len is negative.</p>
- *
- * <pre>
- * StringUtils.left(null, *) = null
- * StringUtils.left(*, -ve) = ""
- * StringUtils.left("", *) = ""
- * StringUtils.left("abc", 0) = ""
- * StringUtils.left("abc", 2) = "ab"
- * StringUtils.left("abc", 4) = "abc"
- * </pre>
- *
- * @param str the String to get the leftmost characters from, may be null
- * @param len the length of the required String
- * @return the leftmost characters, {@code null} if null String input
- */
- public static String left(String str, int len) {
- if (str == null) {
- return null;
- }
- if (len < 0) {
- return EMPTY;
- }
- if (str.length() <= len) {
- return str;
- }
- return str.substring(0, len);
- }
-
- /**
- * <p>Gets the rightmost {@code len} characters of a String.</p>
- *
- * <p>If {@code len} characters are not available, or the String
- * is {@code null}, the String will be returned without an
- * an exception. An empty String is returned if len is negative.</p>
- *
- * <pre>
- * StringUtils.right(null, *) = null
- * StringUtils.right(*, -ve) = ""
- * StringUtils.right("", *) = ""
- * StringUtils.right("abc", 0) = ""
- * StringUtils.right("abc", 2) = "bc"
- * StringUtils.right("abc", 4) = "abc"
- * </pre>
- *
- * @param str the String to get the rightmost characters from, may be null
- * @param len the length of the required String
- * @return the rightmost characters, {@code null} if null String input
- */
- public static String right(String str, int len) {
- if (str == null) {
- return null;
- }
- if (len < 0) {
- return EMPTY;
- }
- if (str.length() <= len) {
- return str;
- }
- return str.substring(str.length() - len);
- }
-
- /**
- * <p>Gets {@code len} characters from the middle of a String.</p>
- *
- * <p>If {@code len} characters are not available, the remainder
- * of the String will be returned without an exception. If the
- * String is {@code null}, {@code null} will be returned.
- * An empty String is returned if len is negative or exceeds the
- * length of {@code str}.</p>
- *
- * <pre>
- * StringUtils.mid(null, *, *) = null
- * StringUtils.mid(*, *, -ve) = ""
- * StringUtils.mid("", 0, *) = ""
- * StringUtils.mid("abc", 0, 2) = "ab"
- * StringUtils.mid("abc", 0, 4) = "abc"
- * StringUtils.mid("abc", 2, 4) = "c"
- * StringUtils.mid("abc", 4, 2) = ""
- * StringUtils.mid("abc", -2, 2) = "ab"
- * </pre>
- *
- * @param str the String to get the characters from, may be null
- * @param pos the position to start from, negative treated as zero
- * @param len the length of the required String
- * @return the middle characters, {@code null} if null String input
- */
- public static String mid(String str, int pos, int len) {
- if (str == null) {
- return null;
- }
- if (len < 0 || pos > str.length()) {
- return EMPTY;
- }
- if (pos < 0) {
- pos = 0;
- }
- if (str.length() <= (pos + len)) {
- return str.substring(pos);
- }
- return str.substring(pos, pos + len);
- }
-
- // SubStringAfter/SubStringBefore
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the substring before the first occurrence of a separator.
- * The separator is not returned.</p>
- *
- * <p>A {@code null} string input will return {@code null}.
- * An empty ("") string input will return the empty string.
- * A {@code null} separator will return the input string.</p>
- *
- * <p>If nothing is found, the string input is returned.</p>
- *
- * <pre>
- * StringUtils.substringBefore(null, *) = null
- * StringUtils.substringBefore("", *) = ""
- * StringUtils.substringBefore("abc", "a") = ""
- * StringUtils.substringBefore("abcba", "b") = "a"
- * StringUtils.substringBefore("abc", "c") = "ab"
- * StringUtils.substringBefore("abc", "d") = "abc"
- * StringUtils.substringBefore("abc", "") = ""
- * StringUtils.substringBefore("abc", null) = "abc"
- * </pre>
- *
- * @param str the String to get a substring from, may be null
- * @param separator the String to search for, may be null
- * @return the substring before the first occurrence of the separator,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String substringBefore(String str, String separator) {
- if (isEmpty(str) || separator == null) {
- return str;
- }
- if (separator.length() == 0) {
- return EMPTY;
- }
- int pos = str.indexOf(separator);
- if (pos == INDEX_NOT_FOUND) {
- return str;
- }
- return str.substring(0, pos);
- }
-
- /**
- * <p>Gets the substring after the first occurrence of a separator.
- * The separator is not returned.</p>
- *
- * <p>A {@code null} string input will return {@code null}.
- * An empty ("") string input will return the empty string.
- * A {@code null} separator will return the empty string if the
- * input string is not {@code null}.</p>
- *
- * <p>If nothing is found, the empty string is returned.</p>
- *
- * <pre>
- * StringUtils.substringAfter(null, *) = null
- * StringUtils.substringAfter("", *) = ""
- * StringUtils.substringAfter(*, null) = ""
- * StringUtils.substringAfter("abc", "a") = "bc"
- * StringUtils.substringAfter("abcba", "b") = "cba"
- * StringUtils.substringAfter("abc", "c") = ""
- * StringUtils.substringAfter("abc", "d") = ""
- * StringUtils.substringAfter("abc", "") = "abc"
- * </pre>
- *
- * @param str the String to get a substring from, may be null
- * @param separator the String to search for, may be null
- * @return the substring after the first occurrence of the separator,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String substringAfter(String str, String separator) {
- if (isEmpty(str)) {
- return str;
- }
- if (separator == null) {
- return EMPTY;
- }
- int pos = str.indexOf(separator);
- if (pos == INDEX_NOT_FOUND) {
- return EMPTY;
- }
- return str.substring(pos + separator.length());
- }
-
- /**
- * <p>Gets the substring before the last occurrence of a separator.
- * The separator is not returned.</p>
- *
- * <p>A {@code null} string input will return {@code null}.
- * An empty ("") string input will return the empty string.
- * An empty or {@code null} separator will return the input string.</p>
- *
- * <p>If nothing is found, the string input is returned.</p>
- *
- * <pre>
- * StringUtils.substringBeforeLast(null, *) = null
- * StringUtils.substringBeforeLast("", *) = ""
- * StringUtils.substringBeforeLast("abcba", "b") = "abc"
- * StringUtils.substringBeforeLast("abc", "c") = "ab"
- * StringUtils.substringBeforeLast("a", "a") = ""
- * StringUtils.substringBeforeLast("a", "z") = "a"
- * StringUtils.substringBeforeLast("a", null) = "a"
- * StringUtils.substringBeforeLast("a", "") = "a"
- * </pre>
- *
- * @param str the String to get a substring from, may be null
- * @param separator the String to search for, may be null
- * @return the substring before the last occurrence of the separator,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String substringBeforeLast(String str, String separator) {
- if (isEmpty(str) || isEmpty(separator)) {
- return str;
- }
- int pos = str.lastIndexOf(separator);
- if (pos == INDEX_NOT_FOUND) {
- return str;
- }
- return str.substring(0, pos);
- }
-
- /**
- * <p>Gets the substring after the last occurrence of a separator.
- * The separator is not returned.</p>
- *
- * <p>A {@code null} string input will return {@code null}.
- * An empty ("") string input will return the empty string.
- * An empty or {@code null} separator will return the empty string if
- * the input string is not {@code null}.</p>
- *
- * <p>If nothing is found, the empty string is returned.</p>
- *
- * <pre>
- * StringUtils.substringAfterLast(null, *) = null
- * StringUtils.substringAfterLast("", *) = ""
- * StringUtils.substringAfterLast(*, "") = ""
- * StringUtils.substringAfterLast(*, null) = ""
- * StringUtils.substringAfterLast("abc", "a") = "bc"
- * StringUtils.substringAfterLast("abcba", "b") = "a"
- * StringUtils.substringAfterLast("abc", "c") = ""
- * StringUtils.substringAfterLast("a", "a") = ""
- * StringUtils.substringAfterLast("a", "z") = ""
- * </pre>
- *
- * @param str the String to get a substring from, may be null
- * @param separator the String to search for, may be null
- * @return the substring after the last occurrence of the separator,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String substringAfterLast(String str, String separator) {
- if (isEmpty(str)) {
- return str;
- }
- if (isEmpty(separator)) {
- return EMPTY;
- }
- int pos = str.lastIndexOf(separator);
- if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) {
- return EMPTY;
- }
- return str.substring(pos + separator.length());
- }
-
- // Substring between
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the String that is nested in between two instances of the
- * same String.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} tag returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.substringBetween(null, *) = null
- * StringUtils.substringBetween("", "") = ""
- * StringUtils.substringBetween("", "tag") = null
- * StringUtils.substringBetween("tagabctag", null) = null
- * StringUtils.substringBetween("tagabctag", "") = ""
- * StringUtils.substringBetween("tagabctag", "tag") = "abc"
- * </pre>
- *
- * @param str the String containing the substring, may be null
- * @param tag the String before and after the substring, may be null
- * @return the substring, {@code null} if no match
- * @since 2.0
- */
- public static String substringBetween(String str, String tag) {
- return substringBetween(str, tag, tag);
- }
-
- /**
- * <p>Gets the String that is nested in between two Strings.
- * Only the first match is returned.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} open/close returns {@code null} (no match).
- * An empty ("") open and close returns an empty string.</p>
- *
- * <pre>
- * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
- * StringUtils.substringBetween(null, *, *) = null
- * StringUtils.substringBetween(*, null, *) = null
- * StringUtils.substringBetween(*, *, null) = null
- * StringUtils.substringBetween("", "", "") = ""
- * StringUtils.substringBetween("", "", "]") = null
- * StringUtils.substringBetween("", "[", "]") = null
- * StringUtils.substringBetween("yabcz", "", "") = ""
- * StringUtils.substringBetween("yabcz", "y", "z") = "abc"
- * StringUtils.substringBetween("yabczyabcz", "y", "z") = "abc"
- * </pre>
- *
- * @param str the String containing the substring, may be null
- * @param open the String before the substring, may be null
- * @param close the String after the substring, may be null
- * @return the substring, {@code null} if no match
- * @since 2.0
- */
- public static String substringBetween(String str, String open, String close) {
- if (str == null || open == null || close == null) {
- return null;
- }
- int start = str.indexOf(open);
- if (start != INDEX_NOT_FOUND) {
- int end = str.indexOf(close, start + open.length());
- if (end != INDEX_NOT_FOUND) {
- return str.substring(start + open.length(), end);
- }
- }
- return null;
- }
-
- /**
- * <p>Searches a String for substrings delimited by a start and end tag,
- * returning all matching substrings in an array.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} open/close returns {@code null} (no match).
- * An empty ("") open/close returns {@code null} (no match).</p>
- *
- * <pre>
- * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
- * StringUtils.substringsBetween(null, *, *) = null
- * StringUtils.substringsBetween(*, null, *) = null
- * StringUtils.substringsBetween(*, *, null) = null
- * StringUtils.substringsBetween("", "[", "]") = []
- * </pre>
- *
- * @param str the String containing the substrings, null returns null, empty returns empty
- * @param open the String identifying the start of the substring, empty returns null
- * @param close the String identifying the end of the substring, empty returns null
- * @return a String Array of substrings, or {@code null} if no match
- * @since 2.3
- */
- public static String[] substringsBetween(String str, String open, String close) {
- if (str == null || isEmpty(open) || isEmpty(close)) {
- return null;
- }
- int strLen = str.length();
- if (strLen == 0) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- int closeLen = close.length();
- int openLen = open.length();
- List<String> list = new ArrayList<String>();
- int pos = 0;
- while (pos < (strLen - closeLen)) {
- int start = str.indexOf(open, pos);
- if (start < 0) {
- break;
- }
- start += openLen;
- int end = str.indexOf(close, start);
- if (end < 0) {
- break;
- }
- list.add(str.substring(start, end));
- pos = end + closeLen;
- }
- if (list.isEmpty()) {
- return null;
- }
- return list.toArray(new String [list.size()]);
- }
-
- // Nested extraction
- //-----------------------------------------------------------------------
-
- // Splitting
- //-----------------------------------------------------------------------
- /**
- * <p>Splits the provided text into an array, using whitespace as the
- * separator.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as one separator.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.split(null) = null
- * StringUtils.split("") = []
- * StringUtils.split("abc def") = ["abc", "def"]
- * StringUtils.split("abc def") = ["abc", "def"]
- * StringUtils.split(" abc ") = ["abc"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @return an array of parsed Strings, {@code null} if null String input
- */
- public static String[] split(String str) {
- return split(str, null, -1);
- }
-
- /**
- * <p>Splits the provided text into an array, separator specified.
- * This is an alternative to using StringTokenizer.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as one separator.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.split(null, *) = null
- * StringUtils.split("", *) = []
- * StringUtils.split("a.b.c", '.') = ["a", "b", "c"]
- * StringUtils.split("a..b.c", '.') = ["a", "b", "c"]
- * StringUtils.split("a:b:c", '.') = ["a:b:c"]
- * StringUtils.split("a b c", ' ') = ["a", "b", "c"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separatorChar the character used as the delimiter
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.0
- */
- public static String[] split(String str, char separatorChar) {
- return splitWorker(str, separatorChar, false);
- }
-
- /**
- * <p>Splits the provided text into an array, separators specified.
- * This is an alternative to using StringTokenizer.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as one separator.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separatorChars splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.split(null, *) = null
- * StringUtils.split("", *) = []
- * StringUtils.split("abc def", null) = ["abc", "def"]
- * StringUtils.split("abc def", " ") = ["abc", "def"]
- * StringUtils.split("abc def", " ") = ["abc", "def"]
- * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separatorChars the characters used as the delimiters,
- * {@code null} splits on whitespace
- * @return an array of parsed Strings, {@code null} if null String input
- */
- public static String[] split(String str, String separatorChars) {
- return splitWorker(str, separatorChars, -1, false);
- }
-
- /**
- * <p>Splits the provided text into an array with a maximum length,
- * separators specified.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as one separator.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separatorChars splits on whitespace.</p>
- *
- * <p>If more than {@code max} delimited substrings are found, the last
- * returned string includes all characters after the first {@code max - 1}
- * returned strings (including separator characters).</p>
- *
- * <pre>
- * StringUtils.split(null, *, *) = null
- * StringUtils.split("", *, *) = []
- * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
- * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"]
- * StringUtils.split("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"]
- * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separatorChars the characters used as the delimiters,
- * {@code null} splits on whitespace
- * @param max the maximum number of elements to include in the
- * array. A zero or negative value implies no limit
- * @return an array of parsed Strings, {@code null} if null String input
- */
- public static String[] split(String str, String separatorChars, int max) {
- return splitWorker(str, separatorChars, max, false);
- }
-
- /**
- * <p>Splits the provided text into an array, separator string specified.</p>
- *
- * <p>The separator(s) will not be included in the returned String array.
- * Adjacent separators are treated as one separator.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separator splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.splitByWholeSeparator(null, *) = null
- * StringUtils.splitByWholeSeparator("", *) = []
- * StringUtils.splitByWholeSeparator("ab de fg", null) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparator("ab de fg", null) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparator("ab:cd:ef", ":") = ["ab", "cd", "ef"]
- * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separator String containing the String to be used as a delimiter,
- * {@code null} splits on whitespace
- * @return an array of parsed Strings, {@code null} if null String was input
- */
- public static String[] splitByWholeSeparator(String str, String separator) {
- return splitByWholeSeparatorWorker( str, separator, -1, false ) ;
- }
-
- /**
- * <p>Splits the provided text into an array, separator string specified.
- * Returns a maximum of {@code max} substrings.</p>
- *
- * <p>The separator(s) will not be included in the returned String array.
- * Adjacent separators are treated as one separator.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separator splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.splitByWholeSeparator(null, *, *) = null
- * StringUtils.splitByWholeSeparator("", *, *) = []
- * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
- * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
- * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separator String containing the String to be used as a delimiter,
- * {@code null} splits on whitespace
- * @param max the maximum number of elements to include in the returned
- * array. A zero or negative value implies no limit.
- * @return an array of parsed Strings, {@code null} if null String was input
- */
- public static String[] splitByWholeSeparator( String str, String separator, int max ) {
- return splitByWholeSeparatorWorker(str, separator, max, false);
- }
-
- /**
- * <p>Splits the provided text into an array, separator string specified. </p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separator splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *) = null
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *) = []
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null) = ["ab", "", "", "de", "fg"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separator String containing the String to be used as a delimiter,
- * {@code null} splits on whitespace
- * @return an array of parsed Strings, {@code null} if null String was input
- * @since 2.4
- */
- public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator) {
- return splitByWholeSeparatorWorker(str, separator, -1, true);
- }
-
- /**
- * <p>Splits the provided text into an array, separator string specified.
- * Returns a maximum of {@code max} substrings.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separator splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *) = null
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *) = []
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0) = ["ab", "de", "fg"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0) = ["ab", "", "", "de", "fg"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
- * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
- * </pre>
- *
- * @param str the String to parse, may be null
- * @param separator String containing the String to be used as a delimiter,
- * {@code null} splits on whitespace
- * @param max the maximum number of elements to include in the returned
- * array. A zero or negative value implies no limit.
- * @return an array of parsed Strings, {@code null} if null String was input
- * @since 2.4
- */
- public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator, int max) {
- return splitByWholeSeparatorWorker(str, separator, max, true);
- }
-
- /**
- * Performs the logic for the {@code splitByWholeSeparatorPreserveAllTokens} methods.
- *
- * @param str the String to parse, may be {@code null}
- * @param separator String containing the String to be used as a delimiter,
- * {@code null} splits on whitespace
- * @param max the maximum number of elements to include in the returned
- * array. A zero or negative value implies no limit.
- * @param preserveAllTokens if {@code true}, adjacent separators are
- * treated as empty token separators; if {@code false}, adjacent
- * separators are treated as one separator.
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.4
- */
- private static String[] splitByWholeSeparatorWorker(
- String str, String separator, int max, boolean preserveAllTokens) {
- if (str == null) {
- return null;
- }
-
- int len = str.length();
-
- if (len == 0) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
-
- if ((separator == null) || (EMPTY.equals(separator))) {
- // Split on whitespace.
- return splitWorker(str, null, max, preserveAllTokens);
- }
-
- int separatorLength = separator.length();
-
- ArrayList<String> substrings = new ArrayList<String>();
- int numberOfSubstrings = 0;
- int beg = 0;
- int end = 0;
- while (end < len) {
- end = str.indexOf(separator, beg);
-
- if (end > -1) {
- if (end > beg) {
- numberOfSubstrings += 1;
-
- if (numberOfSubstrings == max) {
- end = len;
- substrings.add(str.substring(beg));
- } else {
- // The following is OK, because String.substring( beg, end ) excludes
- // the character at the position 'end'.
- substrings.add(str.substring(beg, end));
-
- // Set the starting point for the next search.
- // The following is equivalent to beg = end + (separatorLength - 1) + 1,
- // which is the right calculation:
- beg = end + separatorLength;
- }
- } else {
- // We found a consecutive occurrence of the separator, so skip it.
- if (preserveAllTokens) {
- numberOfSubstrings += 1;
- if (numberOfSubstrings == max) {
- end = len;
- substrings.add(str.substring(beg));
- } else {
- substrings.add(EMPTY);
- }
- }
- beg = end + separatorLength;
- }
- } else {
- // String.substring( beg ) goes from 'beg' to the end of the String.
- substrings.add(str.substring(beg));
- end = len;
- }
- }
-
- return substrings.toArray(new String[substrings.size()]);
- }
-
- // -----------------------------------------------------------------------
- /**
- * <p>Splits the provided text into an array, using whitespace as the
- * separator, preserving all tokens, including empty tokens created by
- * adjacent separators. This is an alternative to using StringTokenizer.
- * Whitespace is defined by {@link Character#isWhitespace(char)}.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.splitPreserveAllTokens(null) = null
- * StringUtils.splitPreserveAllTokens("") = []
- * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "def"]
- * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "", "def"]
- * StringUtils.splitPreserveAllTokens(" abc ") = ["", "abc", ""]
- * </pre>
- *
- * @param str the String to parse, may be {@code null}
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.1
- */
- public static String[] splitPreserveAllTokens(String str) {
- return splitWorker(str, null, -1, true);
- }
-
- /**
- * <p>Splits the provided text into an array, separator specified,
- * preserving all tokens, including empty tokens created by adjacent
- * separators. This is an alternative to using StringTokenizer.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.splitPreserveAllTokens(null, *) = null
- * StringUtils.splitPreserveAllTokens("", *) = []
- * StringUtils.splitPreserveAllTokens("a.b.c", '.') = ["a", "b", "c"]
- * StringUtils.splitPreserveAllTokens("a..b.c", '.') = ["a", "", "b", "c"]
- * StringUtils.splitPreserveAllTokens("a:b:c", '.') = ["a:b:c"]
- * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
- * StringUtils.splitPreserveAllTokens("a b c", ' ') = ["a", "b", "c"]
- * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", ""]
- * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", "", ""]
- * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", a", "b", "c"]
- * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", "", a", "b", "c"]
- * StringUtils.splitPreserveAllTokens(" a b c ", ' ') = ["", a", "b", "c", ""]
- * </pre>
- *
- * @param str the String to parse, may be {@code null}
- * @param separatorChar the character used as the delimiter,
- * {@code null} splits on whitespace
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.1
- */
- public static String[] splitPreserveAllTokens(String str, char separatorChar) {
- return splitWorker(str, separatorChar, true);
- }
-
- /**
- * Performs the logic for the {@code split} and
- * {@code splitPreserveAllTokens} methods that do not return a
- * maximum array length.
- *
- * @param str the String to parse, may be {@code null}
- * @param separatorChar the separate character
- * @param preserveAllTokens if {@code true}, adjacent separators are
- * treated as empty token separators; if {@code false}, adjacent
- * separators are treated as one separator.
- * @return an array of parsed Strings, {@code null} if null String input
- */
- private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) {
- // Performance tuned for 2.0 (JDK1.4)
-
- if (str == null) {
- return null;
- }
- int len = str.length();
- if (len == 0) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- List<String> list = new ArrayList<String>();
- int i = 0, start = 0;
- boolean match = false;
- boolean lastMatch = false;
- while (i < len) {
- if (str.charAt(i) == separatorChar) {
- if (match || preserveAllTokens) {
- list.add(str.substring(start, i));
- match = false;
- lastMatch = true;
- }
- start = ++i;
- continue;
- }
- lastMatch = false;
- match = true;
- i++;
- }
- if (match || (preserveAllTokens && lastMatch)) {
- list.add(str.substring(start, i));
- }
- return list.toArray(new String[list.size()]);
- }
-
- /**
- * <p>Splits the provided text into an array, separators specified,
- * preserving all tokens, including empty tokens created by adjacent
- * separators. This is an alternative to using StringTokenizer.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * For more control over the split use the StrTokenizer class.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separatorChars splits on whitespace.</p>
- *
- * <pre>
- * StringUtils.splitPreserveAllTokens(null, *) = null
- * StringUtils.splitPreserveAllTokens("", *) = []
- * StringUtils.splitPreserveAllTokens("abc def", null) = ["abc", "def"]
- * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "def"]
- * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "", def"]
- * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"]
- * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":") = ["ab", "cd", "ef", ""]
- * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
- * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":") = ["ab", "", cd", "ef"]
- * StringUtils.splitPreserveAllTokens(":cd:ef", ":") = ["", cd", "ef"]
- * StringUtils.splitPreserveAllTokens("::cd:ef", ":") = ["", "", cd", "ef"]
- * StringUtils.splitPreserveAllTokens(":cd:ef:", ":") = ["", cd", "ef", ""]
- * </pre>
- *
- * @param str the String to parse, may be {@code null}
- * @param separatorChars the characters used as the delimiters,
- * {@code null} splits on whitespace
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.1
- */
- public static String[] splitPreserveAllTokens(String str, String separatorChars) {
- return splitWorker(str, separatorChars, -1, true);
- }
-
- /**
- * <p>Splits the provided text into an array with a maximum length,
- * separators specified, preserving all tokens, including empty tokens
- * created by adjacent separators.</p>
- *
- * <p>The separator is not included in the returned String array.
- * Adjacent separators are treated as separators for empty tokens.
- * Adjacent separators are treated as one separator.</p>
- *
- * <p>A {@code null} input String returns {@code null}.
- * A {@code null} separatorChars splits on whitespace.</p>
- *
- * <p>If more than {@code max} delimited substrings are found, the last
- * returned string includes all characters after the first {@code max - 1}
- * returned strings (including separator characters).</p>
- *
- * <pre>
- * StringUtils.splitPreserveAllTokens(null, *, *) = null
- * StringUtils.splitPreserveAllTokens("", *, *) = []
- * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"]
- * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"]
- * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"]
- * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"]
- * StringUtils.splitPreserveAllTokens("ab de fg", null, 2) = ["ab", " de fg"]
- * StringUtils.splitPreserveAllTokens("ab de fg", null, 3) = ["ab", "", " de fg"]
- * StringUtils.splitPreserveAllTokens("ab de fg", null, 4) = ["ab", "", "", "de fg"]
- * </pre>
- *
- * @param str the String to parse, may be {@code null}
- * @param separatorChars the characters used as the delimiters,
- * {@code null} splits on whitespace
- * @param max the maximum number of elements to include in the
- * array. A zero or negative value implies no limit
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.1
- */
- public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) {
- return splitWorker(str, separatorChars, max, true);
- }
-
- /**
- * Performs the logic for the {@code split} and
- * {@code splitPreserveAllTokens} methods that return a maximum array
- * length.
- *
- * @param str the String to parse, may be {@code null}
- * @param separatorChars the separate character
- * @param max the maximum number of elements to include in the
- * array. A zero or negative value implies no limit.
- * @param preserveAllTokens if {@code true}, adjacent separators are
- * treated as empty token separators; if {@code false}, adjacent
- * separators are treated as one separator.
- * @return an array of parsed Strings, {@code null} if null String input
- */
- private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) {
- // Performance tuned for 2.0 (JDK1.4)
- // Direct code is quicker than StringTokenizer.
- // Also, StringTokenizer uses isSpace() not isWhitespace()
-
- if (str == null) {
- return null;
- }
- int len = str.length();
- if (len == 0) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- List<String> list = new ArrayList<String>();
- int sizePlus1 = 1;
- int i = 0, start = 0;
- boolean match = false;
- boolean lastMatch = false;
- if (separatorChars == null) {
- // Null separator means use whitespace
- while (i < len) {
- if (Character.isWhitespace(str.charAt(i))) {
- if (match || preserveAllTokens) {
- lastMatch = true;
- if (sizePlus1++ == max) {
- i = len;
- lastMatch = false;
- }
- list.add(str.substring(start, i));
- match = false;
- }
- start = ++i;
- continue;
- }
- lastMatch = false;
- match = true;
- i++;
- }
- } else if (separatorChars.length() == 1) {
- // Optimise 1 character case
- char sep = separatorChars.charAt(0);
- while (i < len) {
- if (str.charAt(i) == sep) {
- if (match || preserveAllTokens) {
- lastMatch = true;
- if (sizePlus1++ == max) {
- i = len;
- lastMatch = false;
- }
- list.add(str.substring(start, i));
- match = false;
- }
- start = ++i;
- continue;
- }
- lastMatch = false;
- match = true;
- i++;
- }
- } else {
- // standard case
- while (i < len) {
- if (separatorChars.indexOf(str.charAt(i)) >= 0) {
- if (match || preserveAllTokens) {
- lastMatch = true;
- if (sizePlus1++ == max) {
- i = len;
- lastMatch = false;
- }
- list.add(str.substring(start, i));
- match = false;
- }
- start = ++i;
- continue;
- }
- lastMatch = false;
- match = true;
- i++;
- }
- }
- if (match || (preserveAllTokens && lastMatch)) {
- list.add(str.substring(start, i));
- }
- return list.toArray(new String[list.size()]);
- }
-
- /**
- * <p>Splits a String by Character type as returned by
- * {@code java.lang.Character.getType(char)}. Groups of contiguous
- * characters of the same type are returned as complete tokens.
- * <pre>
- * StringUtils.splitByCharacterType(null) = null
- * StringUtils.splitByCharacterType("") = []
- * StringUtils.splitByCharacterType("ab de fg") = ["ab", " ", "de", " ", "fg"]
- * StringUtils.splitByCharacterType("ab de fg") = ["ab", " ", "de", " ", "fg"]
- * StringUtils.splitByCharacterType("ab:cd:ef") = ["ab", ":", "cd", ":", "ef"]
- * StringUtils.splitByCharacterType("number5") = ["number", "5"]
- * StringUtils.splitByCharacterType("fooBar") = ["foo", "B", "ar"]
- * StringUtils.splitByCharacterType("foo200Bar") = ["foo", "200", "B", "ar"]
- * StringUtils.splitByCharacterType("ASFRules") = ["ASFR", "ules"]
- * </pre>
- * @param str the String to split, may be {@code null}
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.4
- */
- public static String[] splitByCharacterType(String str) {
- return splitByCharacterType(str, false);
- }
-
- /**
- * <p>Splits a String by Character type as returned by
- * {@code java.lang.Character.getType(char)}. Groups of contiguous
- * characters of the same type are returned as complete tokens, with the
- * following exception: the character of type
- * {@code Character.UPPERCASE_LETTER}, if any, immediately
- * preceding a token of type {@code Character.LOWERCASE_LETTER}
- * will belong to the following token rather than to the preceding, if any,
- * {@code Character.UPPERCASE_LETTER} token.
- * <pre>
- * StringUtils.splitByCharacterTypeCamelCase(null) = null
- * StringUtils.splitByCharacterTypeCamelCase("") = []
- * StringUtils.splitByCharacterTypeCamelCase("ab de fg") = ["ab", " ", "de", " ", "fg"]
- * StringUtils.splitByCharacterTypeCamelCase("ab de fg") = ["ab", " ", "de", " ", "fg"]
- * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef") = ["ab", ":", "cd", ":", "ef"]
- * StringUtils.splitByCharacterTypeCamelCase("number5") = ["number", "5"]
- * StringUtils.splitByCharacterTypeCamelCase("fooBar") = ["foo", "Bar"]
- * StringUtils.splitByCharacterTypeCamelCase("foo200Bar") = ["foo", "200", "Bar"]
- * StringUtils.splitByCharacterTypeCamelCase("ASFRules") = ["ASF", "Rules"]
- * </pre>
- * @param str the String to split, may be {@code null}
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.4
- */
- public static String[] splitByCharacterTypeCamelCase(String str) {
- return splitByCharacterType(str, true);
- }
-
- /**
- * <p>Splits a String by Character type as returned by
- * {@code java.lang.Character.getType(char)}. Groups of contiguous
- * characters of the same type are returned as complete tokens, with the
- * following exception: if {@code camelCase} is {@code true},
- * the character of type {@code Character.UPPERCASE_LETTER}, if any,
- * immediately preceding a token of type {@code Character.LOWERCASE_LETTER}
- * will belong to the following token rather than to the preceding, if any,
- * {@code Character.UPPERCASE_LETTER} token.
- * @param str the String to split, may be {@code null}
- * @param camelCase whether to use so-called "camel-case" for letter types
- * @return an array of parsed Strings, {@code null} if null String input
- * @since 2.4
- */
- private static String[] splitByCharacterType(String str, boolean camelCase) {
- if (str == null) {
- return null;
- }
- if (str.length() == 0) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- char[] c = str.toCharArray();
- List<String> list = new ArrayList<String>();
- int tokenStart = 0;
- int currentType = Character.getType(c[tokenStart]);
- for (int pos = tokenStart + 1; pos < c.length; pos++) {
- int type = Character.getType(c[pos]);
- if (type == currentType) {
- continue;
- }
- if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) {
- int newTokenStart = pos - 1;
- if (newTokenStart != tokenStart) {
- list.add(new String(c, tokenStart, newTokenStart - tokenStart));
- tokenStart = newTokenStart;
- }
- } else {
- list.add(new String(c, tokenStart, pos - tokenStart));
- tokenStart = pos;
- }
- currentType = type;
- }
- list.add(new String(c, tokenStart, c.length - tokenStart));
- return list.toArray(new String[list.size()]);
- }
-
- // Joining
- //-----------------------------------------------------------------------
- /**
- * <p>Joins the elements of the provided array into a single String
- * containing the provided list of elements.</p>
- *
- * <p>No separator is added to the joined String.
- * Null objects or empty strings within the array are represented by
- * empty strings.</p>
- *
- * <pre>
- * StringUtils.join(null) = null
- * StringUtils.join([]) = ""
- * StringUtils.join([null]) = ""
- * StringUtils.join(["a", "b", "c"]) = "abc"
- * StringUtils.join([null, "", "a"]) = "a"
- * </pre>
- *
- * @param <T> the specific type of values to join together
- * @param elements the values to join together, may be null
- * @return the joined String, {@code null} if null array input
- * @since 2.0
- * @since 3.0 Changed signature to use varargs
- */
- public static <T> String join(T... elements) {
- return join(elements, null);
- }
-
- /**
- * <p>Joins the elements of the provided array into a single String
- * containing the provided list of elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * Null objects or empty strings within the array are represented by
- * empty strings.</p>
- *
- * <pre>
- * StringUtils.join(null, *) = null
- * StringUtils.join([], *) = ""
- * StringUtils.join([null], *) = ""
- * StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
- * StringUtils.join(["a", "b", "c"], null) = "abc"
- * StringUtils.join([null, "", "a"], ';') = ";;a"
- * </pre>
- *
- * @param array the array of values to join together, may be null
- * @param separator the separator character to use
- * @return the joined String, {@code null} if null array input
- * @since 2.0
- */
- public static String join(Object[] array, char separator) {
- if (array == null) {
- return null;
- }
-
- return join(array, separator, 0, array.length);
- }
-
- /**
- * <p>Joins the elements of the provided array into a single String
- * containing the provided list of elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * Null objects or empty strings within the array are represented by
- * empty strings.</p>
- *
- * <pre>
- * StringUtils.join(null, *) = null
- * StringUtils.join([], *) = ""
- * StringUtils.join([null], *) = ""
- * StringUtils.join(["a", "b", "c"], ';') = "a;b;c"
- * StringUtils.join(["a", "b", "c"], null) = "abc"
- * StringUtils.join([null, "", "a"], ';') = ";;a"
- * </pre>
- *
- * @param array the array of values to join together, may be null
- * @param separator the separator character to use
- * @param startIndex the first index to start joining from. It is
- * an error to pass in an end index past the end of the array
- * @param endIndex the index to stop joining from (exclusive). It is
- * an error to pass in an end index past the end of the array
- * @return the joined String, {@code null} if null array input
- * @since 2.0
- */
- public static String join(Object[] array, char separator, int startIndex, int endIndex) {
- if (array == null) {
- return null;
- }
- int noOfItems = (endIndex - startIndex);
- if (noOfItems <= 0) {
- return EMPTY;
- }
-
- StringBuilder buf = new StringBuilder(noOfItems * 16);
-
- for (int i = startIndex; i < endIndex; i++) {
- if (i > startIndex) {
- buf.append(separator);
- }
- if (array[i] != null) {
- buf.append(array[i]);
- }
- }
- return buf.toString();
- }
-
- /**
- * <p>Joins the elements of the provided array into a single String
- * containing the provided list of elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * A {@code null} separator is the same as an empty String ("").
- * Null objects or empty strings within the array are represented by
- * empty strings.</p>
- *
- * <pre>
- * StringUtils.join(null, *) = null
- * StringUtils.join([], *) = ""
- * StringUtils.join([null], *) = ""
- * StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
- * StringUtils.join(["a", "b", "c"], null) = "abc"
- * StringUtils.join(["a", "b", "c"], "") = "abc"
- * StringUtils.join([null, "", "a"], ',') = ",,a"
- * </pre>
- *
- * @param array the array of values to join together, may be null
- * @param separator the separator character to use, null treated as ""
- * @return the joined String, {@code null} if null array input
- */
- public static String join(Object[] array, String separator) {
- if (array == null) {
- return null;
- }
- return join(array, separator, 0, array.length);
- }
-
- /**
- * <p>Joins the elements of the provided array into a single String
- * containing the provided list of elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * A {@code null} separator is the same as an empty String ("").
- * Null objects or empty strings within the array are represented by
- * empty strings.</p>
- *
- * <pre>
- * StringUtils.join(null, *) = null
- * StringUtils.join([], *) = ""
- * StringUtils.join([null], *) = ""
- * StringUtils.join(["a", "b", "c"], "--") = "a--b--c"
- * StringUtils.join(["a", "b", "c"], null) = "abc"
- * StringUtils.join(["a", "b", "c"], "") = "abc"
- * StringUtils.join([null, "", "a"], ',') = ",,a"
- * </pre>
- *
- * @param array the array of values to join together, may be null
- * @param separator the separator character to use, null treated as ""
- * @param startIndex the first index to start joining from. It is
- * an error to pass in an end index past the end of the array
- * @param endIndex the index to stop joining from (exclusive). It is
- * an error to pass in an end index past the end of the array
- * @return the joined String, {@code null} if null array input
- */
- public static String join(Object[] array, String separator, int startIndex, int endIndex) {
- if (array == null) {
- return null;
- }
- if (separator == null) {
- separator = EMPTY;
- }
-
- // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator))
- // (Assuming that all Strings are roughly equally long)
- int noOfItems = (endIndex - startIndex);
- if (noOfItems <= 0) {
- return EMPTY;
- }
-
- StringBuilder buf = new StringBuilder(noOfItems * 16);
-
- for (int i = startIndex; i < endIndex; i++) {
- if (i > startIndex) {
- buf.append(separator);
- }
- if (array[i] != null) {
- buf.append(array[i]);
- }
- }
- return buf.toString();
- }
-
- /**
- * <p>Joins the elements of the provided {@code Iterator} into
- * a single String containing the provided elements.</p>
- *
- * <p>No delimiter is added before or after the list. Null objects or empty
- * strings within the iteration are represented by empty strings.</p>
- *
- * <p>See the examples here: {@link #join(Object[],char)}. </p>
- *
- * @param iterator the {@code Iterator} of values to join together, may be null
- * @param separator the separator character to use
- * @return the joined String, {@code null} if null iterator input
- * @since 2.0
- */
- public static String join(Iterator<?> iterator, char separator) {
-
- // handle null, zero and one elements before building a buffer
- if (iterator == null) {
- return null;
- }
- if (!iterator.hasNext()) {
- return EMPTY;
- }
- Object first = iterator.next();
- if (!iterator.hasNext()) {
- return ObjectUtils.toString(first);
- }
-
- // two or more elements
- StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
- if (first != null) {
- buf.append(first);
- }
-
- while (iterator.hasNext()) {
- buf.append(separator);
- Object obj = iterator.next();
- if (obj != null) {
- buf.append(obj);
- }
- }
-
- return buf.toString();
- }
-
- /**
- * <p>Joins the elements of the provided {@code Iterator} into
- * a single String containing the provided elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * A {@code null} separator is the same as an empty String ("").</p>
- *
- * <p>See the examples here: {@link #join(Object[],String)}. </p>
- *
- * @param iterator the {@code Iterator} of values to join together, may be null
- * @param separator the separator character to use, null treated as ""
- * @return the joined String, {@code null} if null iterator input
- */
- public static String join(Iterator<?> iterator, String separator) {
-
- // handle null, zero and one elements before building a buffer
- if (iterator == null) {
- return null;
- }
- if (!iterator.hasNext()) {
- return EMPTY;
- }
- Object first = iterator.next();
- if (!iterator.hasNext()) {
- return ObjectUtils.toString(first);
- }
-
- // two or more elements
- StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small
- if (first != null) {
- buf.append(first);
- }
-
- while (iterator.hasNext()) {
- if (separator != null) {
- buf.append(separator);
- }
- Object obj = iterator.next();
- if (obj != null) {
- buf.append(obj);
- }
- }
- return buf.toString();
- }
-
- /**
- * <p>Joins the elements of the provided {@code Iterable} into
- * a single String containing the provided elements.</p>
- *
- * <p>No delimiter is added before or after the list. Null objects or empty
- * strings within the iteration are represented by empty strings.</p>
- *
- * <p>See the examples here: {@link #join(Object[],char)}. </p>
- *
- * @param iterable the {@code Iterable} providing the values to join together, may be null
- * @param separator the separator character to use
- * @return the joined String, {@code null} if null iterator input
- * @since 2.3
- */
- public static String join(Iterable<?> iterable, char separator) {
- if (iterable == null) {
- return null;
- }
- return join(iterable.iterator(), separator);
- }
-
- /**
- * <p>Joins the elements of the provided {@code Iterable} into
- * a single String containing the provided elements.</p>
- *
- * <p>No delimiter is added before or after the list.
- * A {@code null} separator is the same as an empty String ("").</p>
- *
- * <p>See the examples here: {@link #join(Object[],String)}. </p>
- *
- * @param iterable the {@code Iterable} providing the values to join together, may be null
- * @param separator the separator character to use, null treated as ""
- * @return the joined String, {@code null} if null iterator input
- * @since 2.3
- */
- public static String join(Iterable<?> iterable, String separator) {
- if (iterable == null) {
- return null;
- }
- return join(iterable.iterator(), separator);
- }
-
- // Delete
- //-----------------------------------------------------------------------
- /**
- * <p>Deletes all whitespaces from a String as defined by
- * {@link Character#isWhitespace(char)}.</p>
- *
- * <pre>
- * StringUtils.deleteWhitespace(null) = null
- * StringUtils.deleteWhitespace("") = ""
- * StringUtils.deleteWhitespace("abc") = "abc"
- * StringUtils.deleteWhitespace(" ab c ") = "abc"
- * </pre>
- *
- * @param str the String to delete whitespace from, may be null
- * @return the String without whitespaces, {@code null} if null String input
- */
- public static String deleteWhitespace(String str) {
- if (isEmpty(str)) {
- return str;
- }
- int sz = str.length();
- char[] chs = new char[sz];
- int count = 0;
- for (int i = 0; i < sz; i++) {
- if (!Character.isWhitespace(str.charAt(i))) {
- chs[count++] = str.charAt(i);
- }
- }
- if (count == sz) {
- return str;
- }
- return new String(chs, 0, count);
- }
-
- // Remove
- //-----------------------------------------------------------------------
- /**
- * <p>Removes a substring only if it is at the beginning of a source string,
- * otherwise returns the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.
- * A {@code null} search string will return the source string.</p>
- *
- * <pre>
- * StringUtils.removeStart(null, *) = null
- * StringUtils.removeStart("", *) = ""
- * StringUtils.removeStart(*, null) = *
- * StringUtils.removeStart("www.domain.com", "www.") = "domain.com"
- * StringUtils.removeStart("domain.com", "www.") = "domain.com"
- * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com"
- * StringUtils.removeStart("abc", "") = "abc"
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the String to search for and remove, may be null
- * @return the substring with the string removed if found,
- * {@code null} if null String input
- * @since 2.1
- */
- public static String removeStart(String str, String remove) {
- if (isEmpty(str) || isEmpty(remove)) {
- return str;
- }
- if (str.startsWith(remove)){
- return str.substring(remove.length());
- }
- return str;
- }
-
- /**
- * <p>Case insensitive removal of a substring if it is at the beginning of a source string,
- * otherwise returns the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.
- * A {@code null} search string will return the source string.</p>
- *
- * <pre>
- * StringUtils.removeStartIgnoreCase(null, *) = null
- * StringUtils.removeStartIgnoreCase("", *) = ""
- * StringUtils.removeStartIgnoreCase(*, null) = *
- * StringUtils.removeStartIgnoreCase("www.domain.com", "www.") = "domain.com"
- * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.") = "domain.com"
- * StringUtils.removeStartIgnoreCase("domain.com", "www.") = "domain.com"
- * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
- * StringUtils.removeStartIgnoreCase("abc", "") = "abc"
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the String to search for (case insensitive) and remove, may be null
- * @return the substring with the string removed if found,
- * {@code null} if null String input
- * @since 2.4
- */
- public static String removeStartIgnoreCase(String str, String remove) {
- if (isEmpty(str) || isEmpty(remove)) {
- return str;
- }
- if (startsWithIgnoreCase(str, remove)) {
- return str.substring(remove.length());
- }
- return str;
- }
-
- /**
- * <p>Removes a substring only if it is at the end of a source string,
- * otherwise returns the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.
- * A {@code null} search string will return the source string.</p>
- *
- * <pre>
- * StringUtils.removeEnd(null, *) = null
- * StringUtils.removeEnd("", *) = ""
- * StringUtils.removeEnd(*, null) = *
- * StringUtils.removeEnd("www.domain.com", ".com.") = "www.domain.com"
- * StringUtils.removeEnd("www.domain.com", ".com") = "www.domain"
- * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
- * StringUtils.removeEnd("abc", "") = "abc"
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the String to search for and remove, may be null
- * @return the substring with the string removed if found,
- * {@code null} if null String input
- * @since 2.1
- */
- public static String removeEnd(String str, String remove) {
- if (isEmpty(str) || isEmpty(remove)) {
- return str;
- }
- if (str.endsWith(remove)) {
- return str.substring(0, str.length() - remove.length());
- }
- return str;
- }
-
- /**
- * <p>Case insensitive removal of a substring if it is at the end of a source string,
- * otherwise returns the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.
- * A {@code null} search string will return the source string.</p>
- *
- * <pre>
- * StringUtils.removeEndIgnoreCase(null, *) = null
- * StringUtils.removeEndIgnoreCase("", *) = ""
- * StringUtils.removeEndIgnoreCase(*, null) = *
- * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.") = "www.domain.com"
- * StringUtils.removeEndIgnoreCase("www.domain.com", ".com") = "www.domain"
- * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
- * StringUtils.removeEndIgnoreCase("abc", "") = "abc"
- * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
- * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the String to search for (case insensitive) and remove, may be null
- * @return the substring with the string removed if found,
- * {@code null} if null String input
- * @since 2.4
- */
- public static String removeEndIgnoreCase(String str, String remove) {
- if (isEmpty(str) || isEmpty(remove)) {
- return str;
- }
- if (endsWithIgnoreCase(str, remove)) {
- return str.substring(0, str.length() - remove.length());
- }
- return str;
- }
-
- /**
- * <p>Removes all occurrences of a substring from within the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.
- * A {@code null} remove string will return the source string.
- * An empty ("") remove string will return the source string.</p>
- *
- * <pre>
- * StringUtils.remove(null, *) = null
- * StringUtils.remove("", *) = ""
- * StringUtils.remove(*, null) = *
- * StringUtils.remove(*, "") = *
- * StringUtils.remove("queued", "ue") = "qd"
- * StringUtils.remove("queued", "zz") = "queued"
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the String to search for and remove, may be null
- * @return the substring with the string removed if found,
- * {@code null} if null String input
- * @since 2.1
- */
- public static String remove(String str, String remove) {
- if (isEmpty(str) || isEmpty(remove)) {
- return str;
- }
- return replace(str, remove, EMPTY, -1);
- }
-
- /**
- * <p>Removes all occurrences of a character from within the source string.</p>
- *
- * <p>A {@code null} source string will return {@code null}.
- * An empty ("") source string will return the empty string.</p>
- *
- * <pre>
- * StringUtils.remove(null, *) = null
- * StringUtils.remove("", *) = ""
- * StringUtils.remove("queued", 'u') = "qeed"
- * StringUtils.remove("queued", 'z') = "queued"
- * </pre>
- *
- * @param str the source String to search, may be null
- * @param remove the char to search for and remove, may be null
- * @return the substring with the char removed if found,
- * {@code null} if null String input
- * @since 2.1
- */
- public static String remove(String str, char remove) {
- if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) {
- return str;
- }
- char[] chars = str.toCharArray();
- int pos = 0;
- for (int i = 0; i < chars.length; i++) {
- if (chars[i] != remove) {
- chars[pos++] = chars[i];
- }
- }
- return new String(chars, 0, pos);
- }
-
- // Replacing
- //-----------------------------------------------------------------------
- /**
- * <p>Replaces a String with another String inside a larger String, once.</p>
- *
- * <p>A {@code null} reference passed to this method is a no-op.</p>
- *
- * <pre>
- * StringUtils.replaceOnce(null, *, *) = null
- * StringUtils.replaceOnce("", *, *) = ""
- * StringUtils.replaceOnce("any", null, *) = "any"
- * StringUtils.replaceOnce("any", *, null) = "any"
- * StringUtils.replaceOnce("any", "", *) = "any"
- * StringUtils.replaceOnce("aba", "a", null) = "aba"
- * StringUtils.replaceOnce("aba", "a", "") = "ba"
- * StringUtils.replaceOnce("aba", "a", "z") = "zba"
- * </pre>
- *
- * @see #replace(String text, String searchString, String replacement, int max)
- * @param text text to search and replace in, may be null
- * @param searchString the String to search for, may be null
- * @param replacement the String to replace with, may be null
- * @return the text with any replacements processed,
- * {@code null} if null String input
- */
- public static String replaceOnce(String text, String searchString, String replacement) {
- return replace(text, searchString, replacement, 1);
- }
-
- /**
- * <p>Replaces all occurrences of a String within another String.</p>
- *
- * <p>A {@code null} reference passed to this method is a no-op.</p>
- *
- * <pre>
- * StringUtils.replace(null, *, *) = null
- * StringUtils.replace("", *, *) = ""
- * StringUtils.replace("any", null, *) = "any"
- * StringUtils.replace("any", *, null) = "any"
- * StringUtils.replace("any", "", *) = "any"
- * StringUtils.replace("aba", "a", null) = "aba"
- * StringUtils.replace("aba", "a", "") = "b"
- * StringUtils.replace("aba", "a", "z") = "zbz"
- * </pre>
- *
- * @see #replace(String text, String searchString, String replacement, int max)
- * @param text text to search and replace in, may be null
- * @param searchString the String to search for, may be null
- * @param replacement the String to replace it with, may be null
- * @return the text with any replacements processed,
- * {@code null} if null String input
- */
- public static String replace(String text, String searchString, String replacement) {
- return replace(text, searchString, replacement, -1);
- }
-
- /**
- * <p>Replaces a String with another String inside a larger String,
- * for the first {@code max} values of the search String.</p>
- *
- * <p>A {@code null} reference passed to this method is a no-op.</p>
- *
- * <pre>
- * StringUtils.replace(null, *, *, *) = null
- * StringUtils.replace("", *, *, *) = ""
- * StringUtils.replace("any", null, *, *) = "any"
- * StringUtils.replace("any", *, null, *) = "any"
- * StringUtils.replace("any", "", *, *) = "any"
- * StringUtils.replace("any", *, *, 0) = "any"
- * StringUtils.replace("abaa", "a", null, -1) = "abaa"
- * StringUtils.replace("abaa", "a", "", -1) = "b"
- * StringUtils.replace("abaa", "a", "z", 0) = "abaa"
- * StringUtils.replace("abaa", "a", "z", 1) = "zbaa"
- * StringUtils.replace("abaa", "a", "z", 2) = "zbza"
- * StringUtils.replace("abaa", "a", "z", -1) = "zbzz"
- * </pre>
- *
- * @param text text to search and replace in, may be null
- * @param searchString the String to search for, may be null
- * @param replacement the String to replace it with, may be null
- * @param max maximum number of values to replace, or {@code -1} if no maximum
- * @return the text with any replacements processed,
- * {@code null} if null String input
- */
- public static String replace(String text, String searchString, String replacement, int max) {
- if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) {
- return text;
- }
- int start = 0;
- int end = text.indexOf(searchString, start);
- if (end == INDEX_NOT_FOUND) {
- return text;
- }
- int replLength = searchString.length();
- int increase = replacement.length() - replLength;
- increase = (increase < 0 ? 0 : increase);
- increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
- StringBuilder buf = new StringBuilder(text.length() + increase);
- while (end != INDEX_NOT_FOUND) {
- buf.append(text.substring(start, end)).append(replacement);
- start = end + replLength;
- if (--max == 0) {
- break;
- }
- end = text.indexOf(searchString, start);
- }
- buf.append(text.substring(start));
- return buf.toString();
- }
-
- /**
- * <p>
- * Replaces all occurrences of Strings within another String.
- * </p>
- *
- * <p>
- * A {@code null} reference passed to this method is a no-op, or if
- * any "search string" or "string to replace" is null, that replace will be
- * ignored. This will not repeat. For repeating replaces, call the
- * overloaded method.
- * </p>
- *
- * <pre>
- * StringUtils.replaceEach(null, *, *) = null
- * StringUtils.replaceEach("", *, *) = ""
- * StringUtils.replaceEach("aba", null, null) = "aba"
- * StringUtils.replaceEach("aba", new String[0], null) = "aba"
- * StringUtils.replaceEach("aba", null, new String[0]) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b"
- * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte"
- * (example of how it does not repeat)
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte"
- * </pre>
- *
- * @param text
- * text to search and replace in, no-op if null
- * @param searchList
- * the Strings to search for, no-op if null
- * @param replacementList
- * the Strings to replace them with, no-op if null
- * @return the text with any replacements processed, {@code null} if
- * null String input
- * @throws IllegalArgumentException
- * if the lengths of the arrays are not the same (null is ok,
- * and/or size 0)
- * @since 2.4
- */
- public static String replaceEach(String text, String[] searchList, String[] replacementList) {
- return replaceEach(text, searchList, replacementList, false, 0);
- }
-
- /**
- * <p>
- * Replaces all occurrences of Strings within another String.
- * </p>
- *
- * <p>
- * A {@code null} reference passed to this method is a no-op, or if
- * any "search string" or "string to replace" is null, that replace will be
- * ignored.
- * </p>
- *
- * <pre>
- * StringUtils.replaceEach(null, *, *, *) = null
- * StringUtils.replaceEach("", *, *, *) = ""
- * StringUtils.replaceEach("aba", null, null, *) = "aba"
- * StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
- * StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
- * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
- * (example of how it repeats)
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalStateException
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
- * </pre>
- *
- * @param text
- * text to search and replace in, no-op if null
- * @param searchList
- * the Strings to search for, no-op if null
- * @param replacementList
- * the Strings to replace them with, no-op if null
- * @return the text with any replacements processed, {@code null} if
- * null String input
- * @throws IllegalStateException
- * if the search is repeating and there is an endless loop due
- * to outputs of one being inputs to another
- * @throws IllegalArgumentException
- * if the lengths of the arrays are not the same (null is ok,
- * and/or size 0)
- * @since 2.4
- */
- public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) {
- // timeToLive should be 0 if not used or nothing to replace, else it's
- // the length of the replace array
- int timeToLive = searchList == null ? 0 : searchList.length;
- return replaceEach(text, searchList, replacementList, true, timeToLive);
- }
-
- /**
- * <p>
- * Replaces all occurrences of Strings within another String.
- * </p>
- *
- * <p>
- * A {@code null} reference passed to this method is a no-op, or if
- * any "search string" or "string to replace" is null, that replace will be
- * ignored.
- * </p>
- *
- * <pre>
- * StringUtils.replaceEach(null, *, *, *) = null
- * StringUtils.replaceEach("", *, *, *) = ""
- * StringUtils.replaceEach("aba", null, null, *) = "aba"
- * StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
- * StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
- * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
- * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
- * (example of how it repeats)
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
- * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
- * </pre>
- *
- * @param text
- * text to search and replace in, no-op if null
- * @param searchList
- * the Strings to search for, no-op if null
- * @param replacementList
- * the Strings to replace them with, no-op if null
- * @param repeat if true, then replace repeatedly
- * until there are no more possible replacements or timeToLive < 0
- * @param timeToLive
- * if less than 0 then there is a circular reference and endless
- * loop
- * @return the text with any replacements processed, {@code null} if
- * null String input
- * @throws IllegalStateException
- * if the search is repeating and there is an endless loop due
- * to outputs of one being inputs to another
- * @throws IllegalArgumentException
- * if the lengths of the arrays are not the same (null is ok,
- * and/or size 0)
- * @since 2.4
- */
- private static String replaceEach(
- String text, String[] searchList, String[] replacementList, boolean repeat, int timeToLive) {
-
- // mchyzer Performance note: This creates very few new objects (one major goal)
- // let me know if there are performance requests, we can create a harness to measure
-
- if (text == null || text.length() == 0 || searchList == null ||
- searchList.length == 0 || replacementList == null || replacementList.length == 0) {
- return text;
- }
-
- // if recursing, this shouldn't be less than 0
- if (timeToLive < 0) {
- throw new IllegalStateException("Aborting to protect against StackOverflowError - " +
- "output of one loop is the input of another");
- }
-
- int searchLength = searchList.length;
- int replacementLength = replacementList.length;
-
- // make sure lengths are ok, these need to be equal
- if (searchLength != replacementLength) {
- throw new IllegalArgumentException("Search and Replace array lengths don't match: "
- + searchLength
- + " vs "
- + replacementLength);
- }
-
- // keep track of which still have matches
- boolean[] noMoreMatchesForReplIndex = new boolean[searchLength];
-
- // index on index that the match was found
- int textIndex = -1;
- int replaceIndex = -1;
- int tempIndex = -1;
-
- // index of replace array that will replace the search string found
- // NOTE: logic duplicated below START
- for (int i = 0; i < searchLength; i++) {
- if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
- searchList[i].length() == 0 || replacementList[i] == null) {
- continue;
- }
- tempIndex = text.indexOf(searchList[i]);
-
- // see if we need to keep searching for this
- if (tempIndex == -1) {
- noMoreMatchesForReplIndex[i] = true;
- } else {
- if (textIndex == -1 || tempIndex < textIndex) {
- textIndex = tempIndex;
- replaceIndex = i;
- }
- }
- }
- // NOTE: logic mostly below END
-
- // no search strings found, we are done
- if (textIndex == -1) {
- return text;
- }
-
- int start = 0;
-
- // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit
- int increase = 0;
-
- // count the replacement text elements that are larger than their corresponding text being replaced
- for (int i = 0; i < searchList.length; i++) {
- if (searchList[i] == null || replacementList[i] == null) {
- continue;
- }
- int greater = replacementList[i].length() - searchList[i].length();
- if (greater > 0) {
- increase += 3 * greater; // assume 3 matches
- }
- }
- // have upper-bound at 20% increase, then let Java take over
- increase = Math.min(increase, text.length() / 5);
-
- StringBuilder buf = new StringBuilder(text.length() + increase);
-
- while (textIndex != -1) {
-
- for (int i = start; i < textIndex; i++) {
- buf.append(text.charAt(i));
- }
- buf.append(replacementList[replaceIndex]);
-
- start = textIndex + searchList[replaceIndex].length();
-
- textIndex = -1;
- replaceIndex = -1;
- tempIndex = -1;
- // find the next earliest match
- // NOTE: logic mostly duplicated above START
- for (int i = 0; i < searchLength; i++) {
- if (noMoreMatchesForReplIndex[i] || searchList[i] == null ||
- searchList[i].length() == 0 || replacementList[i] == null) {
- continue;
- }
- tempIndex = text.indexOf(searchList[i], start);
-
- // see if we need to keep searching for this
- if (tempIndex == -1) {
- noMoreMatchesForReplIndex[i] = true;
- } else {
- if (textIndex == -1 || tempIndex < textIndex) {
- textIndex = tempIndex;
- replaceIndex = i;
- }
- }
- }
- // NOTE: logic duplicated above END
-
- }
- int textLength = text.length();
- for (int i = start; i < textLength; i++) {
- buf.append(text.charAt(i));
- }
- String result = buf.toString();
- if (!repeat) {
- return result;
- }
-
- return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1);
- }
-
- // Replace, character based
- //-----------------------------------------------------------------------
- /**
- * <p>Replaces all occurrences of a character in a String with another.
- * This is a null-safe version of {@link String#replace(char, char)}.</p>
- *
- * <p>A {@code null} string input returns {@code null}.
- * An empty ("") string input returns an empty string.</p>
- *
- * <pre>
- * StringUtils.replaceChars(null, *, *) = null
- * StringUtils.replaceChars("", *, *) = ""
- * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya"
- * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba"
- * </pre>
- *
- * @param str String to replace characters in, may be null
- * @param searchChar the character to search for, may be null
- * @param replaceChar the character to replace, may be null
- * @return modified String, {@code null} if null string input
- * @since 2.0
- */
- public static String replaceChars(String str, char searchChar, char replaceChar) {
- if (str == null) {
- return null;
- }
- return str.replace(searchChar, replaceChar);
- }
-
- /**
- * <p>Replaces multiple characters in a String in one go.
- * This method can also be used to delete characters.</p>
- *
- * <p>For example:<br />
- * <code>replaceChars("hello", "ho", "jy") = jelly</code>.</p>
- *
- * <p>A {@code null} string input returns {@code null}.
- * An empty ("") string input returns an empty string.
- * A null or empty set of search characters returns the input string.</p>
- *
- * <p>The length of the search characters should normally equal the length
- * of the replace characters.
- * If the search characters is longer, then the extra search characters
- * are deleted.
- * If the search characters is shorter, then the extra replace characters
- * are ignored.</p>
- *
- * <pre>
- * StringUtils.replaceChars(null, *, *) = null
- * StringUtils.replaceChars("", *, *) = ""
- * StringUtils.replaceChars("abc", null, *) = "abc"
- * StringUtils.replaceChars("abc", "", *) = "abc"
- * StringUtils.replaceChars("abc", "b", null) = "ac"
- * StringUtils.replaceChars("abc", "b", "") = "ac"
- * StringUtils.replaceChars("abcba", "bc", "yz") = "ayzya"
- * StringUtils.replaceChars("abcba", "bc", "y") = "ayya"
- * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya"
- * </pre>
- *
- * @param str String to replace characters in, may be null
- * @param searchChars a set of characters to search for, may be null
- * @param replaceChars a set of characters to replace, may be null
- * @return modified String, {@code null} if null string input
- * @since 2.0
- */
- public static String replaceChars(String str, String searchChars, String replaceChars) {
- if (isEmpty(str) || isEmpty(searchChars)) {
- return str;
- }
- if (replaceChars == null) {
- replaceChars = EMPTY;
- }
- boolean modified = false;
- int replaceCharsLength = replaceChars.length();
- int strLength = str.length();
- StringBuilder buf = new StringBuilder(strLength);
- for (int i = 0; i < strLength; i++) {
- char ch = str.charAt(i);
- int index = searchChars.indexOf(ch);
- if (index >= 0) {
- modified = true;
- if (index < replaceCharsLength) {
- buf.append(replaceChars.charAt(index));
- }
- } else {
- buf.append(ch);
- }
- }
- if (modified) {
- return buf.toString();
- }
- return str;
- }
-
- // Overlay
- //-----------------------------------------------------------------------
- /**
- * <p>Overlays part of a String with another String.</p>
- *
- * <p>A {@code null} string input returns {@code null}.
- * A negative index is treated as zero.
- * An index greater than the string length is treated as the string length.
- * The start index is always the smaller of the two indices.</p>
- *
- * <pre>
- * StringUtils.overlay(null, *, *, *) = null
- * StringUtils.overlay("", "abc", 0, 0) = "abc"
- * StringUtils.overlay("abcdef", null, 2, 4) = "abef"
- * StringUtils.overlay("abcdef", "", 2, 4) = "abef"
- * StringUtils.overlay("abcdef", "", 4, 2) = "abef"
- * StringUtils.overlay("abcdef", "zzzz", 2, 4) = "abzzzzef"
- * StringUtils.overlay("abcdef", "zzzz", 4, 2) = "abzzzzef"
- * StringUtils.overlay("abcdef", "zzzz", -1, 4) = "zzzzef"
- * StringUtils.overlay("abcdef", "zzzz", 2, 8) = "abzzzz"
- * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef"
- * StringUtils.overlay("abcdef", "zzzz", 8, 10) = "abcdefzzzz"
- * </pre>
- *
- * @param str the String to do overlaying in, may be null
- * @param overlay the String to overlay, may be null
- * @param start the position to start overlaying at
- * @param end the position to stop overlaying before
- * @return overlayed String, {@code null} if null String input
- * @since 2.0
- */
- public static String overlay(String str, String overlay, int start, int end) {
- if (str == null) {
- return null;
- }
- if (overlay == null) {
- overlay = EMPTY;
- }
- int len = str.length();
- if (start < 0) {
- start = 0;
- }
- if (start > len) {
- start = len;
- }
- if (end < 0) {
- end = 0;
- }
- if (end > len) {
- end = len;
- }
- if (start > end) {
- int temp = start;
- start = end;
- end = temp;
- }
- return new StringBuilder(len + start - end + overlay.length() + 1)
- .append(str.substring(0, start))
- .append(overlay)
- .append(str.substring(end))
- .toString();
- }
-
- // Chomping
- //-----------------------------------------------------------------------
- /**
- * <p>Removes one newline from end of a String if it's there,
- * otherwise leave it alone. A newline is "{@code \n}",
- * "{@code \r}", or "{@code \r\n}".</p>
- *
- * <p>NOTE: This method changed in 2.0.
- * It now more closely matches Perl chomp.</p>
- *
- * <pre>
- * StringUtils.chomp(null) = null
- * StringUtils.chomp("") = ""
- * StringUtils.chomp("abc \r") = "abc "
- * StringUtils.chomp("abc\n") = "abc"
- * StringUtils.chomp("abc\r\n") = "abc"
- * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n"
- * StringUtils.chomp("abc\n\r") = "abc\n"
- * StringUtils.chomp("abc\n\rabc") = "abc\n\rabc"
- * StringUtils.chomp("\r") = ""
- * StringUtils.chomp("\n") = ""
- * StringUtils.chomp("\r\n") = ""
- * </pre>
- *
- * @param str the String to chomp a newline from, may be null
- * @return String without newline, {@code null} if null String input
- */
- public static String chomp(String str) {
- if (isEmpty(str)) {
- return str;
- }
-
- if (str.length() == 1) {
- char ch = str.charAt(0);
- if (ch == CharUtils.CR || ch == CharUtils.LF) {
- return EMPTY;
- }
- return str;
- }
-
- int lastIdx = str.length() - 1;
- char last = str.charAt(lastIdx);
-
- if (last == CharUtils.LF) {
- if (str.charAt(lastIdx - 1) == CharUtils.CR) {
- lastIdx--;
- }
- } else if (last != CharUtils.CR) {
- lastIdx++;
- }
- return str.substring(0, lastIdx);
- }
-
- // Chopping
- //-----------------------------------------------------------------------
- /**
- * <p>Remove the last character from a String.</p>
- *
- * <p>If the String ends in {@code \r\n}, then remove both
- * of them.</p>
- *
- * <pre>
- * StringUtils.chop(null) = null
- * StringUtils.chop("") = ""
- * StringUtils.chop("abc \r") = "abc "
- * StringUtils.chop("abc\n") = "abc"
- * StringUtils.chop("abc\r\n") = "abc"
- * StringUtils.chop("abc") = "ab"
- * StringUtils.chop("abc\nabc") = "abc\nab"
- * StringUtils.chop("a") = ""
- * StringUtils.chop("\r") = ""
- * StringUtils.chop("\n") = ""
- * StringUtils.chop("\r\n") = ""
- * </pre>
- *
- * @param str the String to chop last character from, may be null
- * @return String without last character, {@code null} if null String input
- */
- public static String chop(String str) {
- if (str == null) {
- return null;
- }
- int strLen = str.length();
- if (strLen < 2) {
- return EMPTY;
- }
- int lastIdx = strLen - 1;
- String ret = str.substring(0, lastIdx);
- char last = str.charAt(lastIdx);
- if (last == CharUtils.LF && ret.charAt(lastIdx - 1) == CharUtils.CR) {
- return ret.substring(0, lastIdx - 1);
- }
- return ret;
- }
-
- // Conversion
- //-----------------------------------------------------------------------
-
- // Padding
- //-----------------------------------------------------------------------
- /**
- * <p>Repeat a String {@code repeat} times to form a
- * new String.</p>
- *
- * <pre>
- * StringUtils.repeat(null, 2) = null
- * StringUtils.repeat("", 0) = ""
- * StringUtils.repeat("", 2) = ""
- * StringUtils.repeat("a", 3) = "aaa"
- * StringUtils.repeat("ab", 2) = "abab"
- * StringUtils.repeat("a", -2) = ""
- * </pre>
- *
- * @param str the String to repeat, may be null
- * @param repeat number of times to repeat str, negative treated as zero
- * @return a new String consisting of the original String repeated,
- * {@code null} if null String input
- */
- public static String repeat(String str, int repeat) {
- // Performance tuned for 2.0 (JDK1.4)
-
- if (str == null) {
- return null;
- }
- if (repeat <= 0) {
- return EMPTY;
- }
- int inputLength = str.length();
- if (repeat == 1 || inputLength == 0) {
- return str;
- }
- if (inputLength == 1 && repeat <= PAD_LIMIT) {
- return repeat(str.charAt(0), repeat);
- }
-
- int outputLength = inputLength * repeat;
- switch (inputLength) {
- case 1 :
- return repeat(str.charAt(0), repeat);
- case 2 :
- char ch0 = str.charAt(0);
- char ch1 = str.charAt(1);
- char[] output2 = new char[outputLength];
- for (int i = repeat * 2 - 2; i >= 0; i--, i--) {
- output2[i] = ch0;
- output2[i + 1] = ch1;
- }
- return new String(output2);
- default :
- StringBuilder buf = new StringBuilder(outputLength);
- for (int i = 0; i < repeat; i++) {
- buf.append(str);
- }
- return buf.toString();
- }
- }
-
- /**
- * <p>Repeat a String {@code repeat} times to form a
- * new String, with a String separator injected each time. </p>
- *
- * <pre>
- * StringUtils.repeat(null, null, 2) = null
- * StringUtils.repeat(null, "x", 2) = null
- * StringUtils.repeat("", null, 0) = ""
- * StringUtils.repeat("", "", 2) = ""
- * StringUtils.repeat("", "x", 3) = "xxx"
- * StringUtils.repeat("?", ", ", 3) = "?, ?, ?"
- * </pre>
- *
- * @param str the String to repeat, may be null
- * @param separator the String to inject, may be null
- * @param repeat number of times to repeat str, negative treated as zero
- * @return a new String consisting of the original String repeated,
- * {@code null} if null String input
- * @since 2.5
- */
- public static String repeat(String str, String separator, int repeat) {
- if(str == null || separator == null) {
- return repeat(str, repeat);
- } else {
- // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it
- String result = repeat(str + separator, repeat);
- return removeEnd(result, separator);
- }
- }
-
- /**
- * <p>Returns padding using the specified delimiter repeated
- * to a given length.</p>
- *
- * <pre>
- * StringUtils.repeat(0, 'e') = ""
- * StringUtils.repeat(3, 'e') = "eee"
- * StringUtils.repeat(-2, 'e') = ""
- * </pre>
- *
- * <p>Note: this method doesn't not support padding with
- * <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
- * as they require a pair of {@code char}s to be represented.
- * If you are needing to support full I18N of your applications
- * consider using {@link #repeat(String, int)} instead.
- * </p>
- *
- * @param ch character to repeat
- * @param repeat number of times to repeat char, negative treated as zero
- * @return String with repeated character
- * @see #repeat(String, int)
- */
- public static String repeat(char ch, int repeat) {
- char[] buf = new char[repeat];
- for (int i = repeat - 1; i >= 0; i--) {
- buf[i] = ch;
- }
- return new String(buf);
- }
-
- /**
- * <p>Right pad a String with spaces (' ').</p>
- *
- * <p>The String is padded to the size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.rightPad(null, *) = null
- * StringUtils.rightPad("", 3) = " "
- * StringUtils.rightPad("bat", 3) = "bat"
- * StringUtils.rightPad("bat", 5) = "bat "
- * StringUtils.rightPad("bat", 1) = "bat"
- * StringUtils.rightPad("bat", -1) = "bat"
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @return right padded String or original String if no padding is necessary,
- * {@code null} if null String input
- */
- public static String rightPad(String str, int size) {
- return rightPad(str, size, ' ');
- }
-
- /**
- * <p>Right pad a String with a specified character.</p>
- *
- * <p>The String is padded to the size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.rightPad(null, *, *) = null
- * StringUtils.rightPad("", 3, 'z') = "zzz"
- * StringUtils.rightPad("bat", 3, 'z') = "bat"
- * StringUtils.rightPad("bat", 5, 'z') = "batzz"
- * StringUtils.rightPad("bat", 1, 'z') = "bat"
- * StringUtils.rightPad("bat", -1, 'z') = "bat"
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @param padChar the character to pad with
- * @return right padded String or original String if no padding is necessary,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String rightPad(String str, int size, char padChar) {
- if (str == null) {
- return null;
- }
- int pads = size - str.length();
- if (pads <= 0) {
- return str; // returns original String when possible
- }
- if (pads > PAD_LIMIT) {
- return rightPad(str, size, String.valueOf(padChar));
- }
- return str.concat(repeat(padChar, pads));
- }
-
- /**
- * <p>Right pad a String with a specified String.</p>
- *
- * <p>The String is padded to the size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.rightPad(null, *, *) = null
- * StringUtils.rightPad("", 3, "z") = "zzz"
- * StringUtils.rightPad("bat", 3, "yz") = "bat"
- * StringUtils.rightPad("bat", 5, "yz") = "batyz"
- * StringUtils.rightPad("bat", 8, "yz") = "batyzyzy"
- * StringUtils.rightPad("bat", 1, "yz") = "bat"
- * StringUtils.rightPad("bat", -1, "yz") = "bat"
- * StringUtils.rightPad("bat", 5, null) = "bat "
- * StringUtils.rightPad("bat", 5, "") = "bat "
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @param padStr the String to pad with, null or empty treated as single space
- * @return right padded String or original String if no padding is necessary,
- * {@code null} if null String input
- */
- public static String rightPad(String str, int size, String padStr) {
- if (str == null) {
- return null;
- }
- if (isEmpty(padStr)) {
- padStr = " ";
- }
- int padLen = padStr.length();
- int strLen = str.length();
- int pads = size - strLen;
- if (pads <= 0) {
- return str; // returns original String when possible
- }
- if (padLen == 1 && pads <= PAD_LIMIT) {
- return rightPad(str, size, padStr.charAt(0));
- }
-
- if (pads == padLen) {
- return str.concat(padStr);
- } else if (pads < padLen) {
- return str.concat(padStr.substring(0, pads));
- } else {
- char[] padding = new char[pads];
- char[] padChars = padStr.toCharArray();
- for (int i = 0; i < pads; i++) {
- padding[i] = padChars[i % padLen];
- }
- return str.concat(new String(padding));
- }
- }
-
- /**
- * <p>Left pad a String with spaces (' ').</p>
- *
- * <p>The String is padded to the size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.leftPad(null, *) = null
- * StringUtils.leftPad("", 3) = " "
- * StringUtils.leftPad("bat", 3) = "bat"
- * StringUtils.leftPad("bat", 5) = " bat"
- * StringUtils.leftPad("bat", 1) = "bat"
- * StringUtils.leftPad("bat", -1) = "bat"
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @return left padded String or original String if no padding is necessary,
- * {@code null} if null String input
- */
- public static String leftPad(String str, int size) {
- return leftPad(str, size, ' ');
- }
-
- /**
- * <p>Left pad a String with a specified character.</p>
- *
- * <p>Pad to a size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.leftPad(null, *, *) = null
- * StringUtils.leftPad("", 3, 'z') = "zzz"
- * StringUtils.leftPad("bat", 3, 'z') = "bat"
- * StringUtils.leftPad("bat", 5, 'z') = "zzbat"
- * StringUtils.leftPad("bat", 1, 'z') = "bat"
- * StringUtils.leftPad("bat", -1, 'z') = "bat"
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @param padChar the character to pad with
- * @return left padded String or original String if no padding is necessary,
- * {@code null} if null String input
- * @since 2.0
- */
- public static String leftPad(String str, int size, char padChar) {
- if (str == null) {
- return null;
- }
- int pads = size - str.length();
- if (pads <= 0) {
- return str; // returns original String when possible
- }
- if (pads > PAD_LIMIT) {
- return leftPad(str, size, String.valueOf(padChar));
- }
- return repeat(padChar, pads).concat(str);
- }
-
- /**
- * <p>Left pad a String with a specified String.</p>
- *
- * <p>Pad to a size of {@code size}.</p>
- *
- * <pre>
- * StringUtils.leftPad(null, *, *) = null
- * StringUtils.leftPad("", 3, "z") = "zzz"
- * StringUtils.leftPad("bat", 3, "yz") = "bat"
- * StringUtils.leftPad("bat", 5, "yz") = "yzbat"
- * StringUtils.leftPad("bat", 8, "yz") = "yzyzybat"
- * StringUtils.leftPad("bat", 1, "yz") = "bat"
- * StringUtils.leftPad("bat", -1, "yz") = "bat"
- * StringUtils.leftPad("bat", 5, null) = " bat"
- * StringUtils.leftPad("bat", 5, "") = " bat"
- * </pre>
- *
- * @param str the String to pad out, may be null
- * @param size the size to pad to
- * @param padStr the String to pad with, null or empty treated as single space
- * @return left padded String or original String if no padding is necessary,
- * {@code null} if null String input
- */
- public static String leftPad(String str, int size, String padStr) {
- if (str == null) {
- return null;
- }
- if (isEmpty(padStr)) {
- padStr = " ";
- }
- int padLen = padStr.length();
- int strLen = str.length();
- int pads = size - strLen;
- if (pads <= 0) {
- return str; // returns original String when possible
- }
- if (padLen == 1 && pads <= PAD_LIMIT) {
- return leftPad(str, size, padStr.charAt(0));
- }
-
- if (pads == padLen) {
- return padStr.concat(str);
- } else if (pads < padLen) {
- return padStr.substring(0, pads).concat(str);
- } else {
- char[] padding = new char[pads];
- char[] padChars = padStr.toCharArray();
- for (int i = 0; i < pads; i++) {
- padding[i] = padChars[i % padLen];
- }
- return new String(padding).concat(str);
- }
- }
-
- /**
- * Gets a CharSequence length or {@code 0} if the CharSequence is
- * {@code null}.
- *
- * @param cs
- * a CharSequence or {@code null}
- * @return CharSequence length or {@code 0} if the CharSequence is
- * {@code null}.
- * @since 2.4
- * @since 3.0 Changed signature from length(String) to length(CharSequence)
- */
- public static int length(CharSequence cs) {
- return cs == null ? 0 : cs.length();
- }
-
- // Centering
- //-----------------------------------------------------------------------
- /**
- * <p>Centers a String in a larger String of size {@code size}
- * using the space character (' ').<p>
- *
- * <p>If the size is less than the String length, the String is returned.
- * A {@code null} String returns {@code null}.
- * A negative size is treated as zero.</p>
- *
- * <p>Equivalent to {@code center(str, size, " ")}.</p>
- *
- * <pre>
- * StringUtils.center(null, *) = null
- * StringUtils.center("", 4) = " "
- * StringUtils.center("ab", -1) = "ab"
- * StringUtils.center("ab", 4) = " ab "
- * StringUtils.center("abcd", 2) = "abcd"
- * StringUtils.center("a", 4) = " a "
- * </pre>
- *
- * @param str the String to center, may be null
- * @param size the int size of new String, negative treated as zero
- * @return centered String, {@code null} if null String input
- */
- public static String center(String str, int size) {
- return center(str, size, ' ');
- }
-
- /**
- * <p>Centers a String in a larger String of size {@code size}.
- * Uses a supplied character as the value to pad the String with.</p>
- *
- * <p>If the size is less than the String length, the String is returned.
- * A {@code null} String returns {@code null}.
- * A negative size is treated as zero.</p>
- *
- * <pre>
- * StringUtils.center(null, *, *) = null
- * StringUtils.center("", 4, ' ') = " "
- * StringUtils.center("ab", -1, ' ') = "ab"
- * StringUtils.center("ab", 4, ' ') = " ab"
- * StringUtils.center("abcd", 2, ' ') = "abcd"
- * StringUtils.center("a", 4, ' ') = " a "
- * StringUtils.center("a", 4, 'y') = "yayy"
- * </pre>
- *
- * @param str the String to center, may be null
- * @param size the int size of new String, negative treated as zero
- * @param padChar the character to pad the new String with
- * @return centered String, {@code null} if null String input
- * @since 2.0
- */
- public static String center(String str, int size, char padChar) {
- if (str == null || size <= 0) {
- return str;
- }
- int strLen = str.length();
- int pads = size - strLen;
- if (pads <= 0) {
- return str;
- }
- str = leftPad(str, strLen + pads / 2, padChar);
- str = rightPad(str, size, padChar);
- return str;
- }
-
- /**
- * <p>Centers a String in a larger String of size {@code size}.
- * Uses a supplied String as the value to pad the String with.</p>
- *
- * <p>If the size is less than the String length, the String is returned.
- * A {@code null} String returns {@code null}.
- * A negative size is treated as zero.</p>
- *
- * <pre>
- * StringUtils.center(null, *, *) = null
- * StringUtils.center("", 4, " ") = " "
- * StringUtils.center("ab", -1, " ") = "ab"
- * StringUtils.center("ab", 4, " ") = " ab"
- * StringUtils.center("abcd", 2, " ") = "abcd"
- * StringUtils.center("a", 4, " ") = " a "
- * StringUtils.center("a", 4, "yz") = "yayz"
- * StringUtils.center("abc", 7, null) = " abc "
- * StringUtils.center("abc", 7, "") = " abc "
- * </pre>
- *
- * @param str the String to center, may be null
- * @param size the int size of new String, negative treated as zero
- * @param padStr the String to pad the new String with, must not be null or empty
- * @return centered String, {@code null} if null String input
- * @throws IllegalArgumentException if padStr is {@code null} or empty
- */
- public static String center(String str, int size, String padStr) {
- if (str == null || size <= 0) {
- return str;
- }
- if (isEmpty(padStr)) {
- padStr = " ";
- }
- int strLen = str.length();
- int pads = size - strLen;
- if (pads <= 0) {
- return str;
- }
- str = leftPad(str, strLen + pads / 2, padStr);
- str = rightPad(str, size, padStr);
- return str;
- }
-
- // Case conversion
- //-----------------------------------------------------------------------
- /**
- * <p>Converts a String to upper case as per {@link String#toUpperCase()}.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.upperCase(null) = null
- * StringUtils.upperCase("") = ""
- * StringUtils.upperCase("aBc") = "ABC"
- * </pre>
- *
- * <p><strong>Note:</strong> As described in the documentation for {@link String#toUpperCase()},
- * the result of this method is affected by the current locale.
- * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)}
- * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p>
- *
- * @param str the String to upper case, may be null
- * @return the upper cased String, {@code null} if null String input
- */
- public static String upperCase(String str) {
- if (str == null) {
- return null;
- }
- return str.toUpperCase();
- }
-
- /**
- * <p>Converts a String to upper case as per {@link String#toUpperCase(Locale)}.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.upperCase(null, Locale.ENGLISH) = null
- * StringUtils.upperCase("", Locale.ENGLISH) = ""
- * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC"
- * </pre>
- *
- * @param str the String to upper case, may be null
- * @param locale the locale that defines the case transformation rules, must not be null
- * @return the upper cased String, {@code null} if null String input
- * @since 2.5
- */
- public static String upperCase(String str, Locale locale) {
- if (str == null) {
- return null;
- }
- return str.toUpperCase(locale);
- }
-
- /**
- * <p>Converts a String to lower case as per {@link String#toLowerCase()}.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.lowerCase(null) = null
- * StringUtils.lowerCase("") = ""
- * StringUtils.lowerCase("aBc") = "abc"
- * </pre>
- *
- * <p><strong>Note:</strong> As described in the documentation for {@link String#toLowerCase()},
- * the result of this method is affected by the current locale.
- * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)}
- * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p>
- *
- * @param str the String to lower case, may be null
- * @return the lower cased String, {@code null} if null String input
- */
- public static String lowerCase(String str) {
- if (str == null) {
- return null;
- }
- return str.toLowerCase();
- }
-
- /**
- * <p>Converts a String to lower case as per {@link String#toLowerCase(Locale)}.</p>
- *
- * <p>A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.lowerCase(null, Locale.ENGLISH) = null
- * StringUtils.lowerCase("", Locale.ENGLISH) = ""
- * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc"
- * </pre>
- *
- * @param str the String to lower case, may be null
- * @param locale the locale that defines the case transformation rules, must not be null
- * @return the lower cased String, {@code null} if null String input
- * @since 2.5
- */
- public static String lowerCase(String str, Locale locale) {
- if (str == null) {
- return null;
- }
- return str.toLowerCase(locale);
- }
-
- /**
- * <p>Capitalizes a String changing the first letter to title case as
- * per {@link Character#toTitleCase(char)}. No other letters are changed.</p>
- *
- * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}.
- * A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.capitalize(null) = null
- * StringUtils.capitalize("") = ""
- * StringUtils.capitalize("cat") = "Cat"
- * StringUtils.capitalize("cAt") = "CAt"
- * </pre>
- *
- * @param str the String to capitalize, may be null
- * @return the capitalized String, {@code null} if null String input
- * @see org.apache.commons.lang3.text.WordUtils#capitalize(String)
- * @see #uncapitalize(String)
- * @since 2.0
- */
- public static String capitalize(String str) {
- int strLen;
- if (str == null || (strLen = str.length()) == 0) {
- return str;
- }
- return new StringBuilder(strLen)
- .append(Character.toTitleCase(str.charAt(0)))
- .append(str.substring(1))
- .toString();
- }
-
- /**
- * <p>Uncapitalizes a String changing the first letter to title case as
- * per {@link Character#toLowerCase(char)}. No other letters are changed.</p>
- *
- * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}.
- * A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.uncapitalize(null) = null
- * StringUtils.uncapitalize("") = ""
- * StringUtils.uncapitalize("Cat") = "cat"
- * StringUtils.uncapitalize("CAT") = "cAT"
- * </pre>
- *
- * @param str the String to uncapitalize, may be null
- * @return the uncapitalized String, {@code null} if null String input
- * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String)
- * @see #capitalize(String)
- * @since 2.0
- */
- public static String uncapitalize(String str) {
- int strLen;
- if (str == null || (strLen = str.length()) == 0) {
- return str;
- }
- return new StringBuilder(strLen)
- .append(Character.toLowerCase(str.charAt(0)))
- .append(str.substring(1))
- .toString();
- }
-
- /**
- * <p>Swaps the case of a String changing upper and title case to
- * lower case, and lower case to upper case.</p>
- *
- * <ul>
- * <li>Upper case character converts to Lower case</li>
- * <li>Title case character converts to Lower case</li>
- * <li>Lower case character converts to Upper case</li>
- * </ul>
- *
- * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}.
- * A {@code null} input String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.swapCase(null) = null
- * StringUtils.swapCase("") = ""
- * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
- * </pre>
- *
- * <p>NOTE: This method changed in Lang version 2.0.
- * It no longer performs a word based algorithm.
- * If you only use ASCII, you will notice no change.
- * That functionality is available in org.apache.commons.lang3.text.WordUtils.</p>
- *
- * @param str the String to swap case, may be null
- * @return the changed String, {@code null} if null String input
- */
- public static String swapCase(String str) {
- if (StringUtils.isEmpty(str)) {
- return str;
- }
-
- char[] buffer = str.toCharArray();
-
- for (int i = 0; i < buffer.length; i++) {
- char ch = buffer[i];
- if (Character.isUpperCase(ch)) {
- buffer[i] = Character.toLowerCase(ch);
- } else if (Character.isTitleCase(ch)) {
- buffer[i] = Character.toLowerCase(ch);
- } else if (Character.isLowerCase(ch)) {
- buffer[i] = Character.toUpperCase(ch);
- }
- }
- return new String(buffer);
- }
-
- // Count matches
- //-----------------------------------------------------------------------
- /**
- * <p>Counts how many times the substring appears in the larger string.</p>
- *
- * <p>A {@code null} or empty ("") String input returns {@code 0}.</p>
- *
- * <pre>
- * StringUtils.countMatches(null, *) = 0
- * StringUtils.countMatches("", *) = 0
- * StringUtils.countMatches("abba", null) = 0
- * StringUtils.countMatches("abba", "") = 0
- * StringUtils.countMatches("abba", "a") = 2
- * StringUtils.countMatches("abba", "ab") = 1
- * StringUtils.countMatches("abba", "xxx") = 0
- * </pre>
- *
- * @param str the CharSequence to check, may be null
- * @param sub the substring to count, may be null
- * @return the number of occurrences, 0 if either CharSequence is {@code null}
- * @since 3.0 Changed signature from countMatches(String, String) to countMatches(CharSequence, CharSequence)
- */
- public static int countMatches(CharSequence str, CharSequence sub) {
- if (isEmpty(str) || isEmpty(sub)) {
- return 0;
- }
- int count = 0;
- int idx = 0;
- while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) {
- count++;
- idx += sub.length();
- }
- return count;
- }
-
- // Character Tests
- //-----------------------------------------------------------------------
- /**
- * <p>Checks if the CharSequence contains only Unicode letters.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.isAlpha(null) = false
- * StringUtils.isAlpha("") = false
- * StringUtils.isAlpha(" ") = false
- * StringUtils.isAlpha("abc") = true
- * StringUtils.isAlpha("ab2c") = false
- * StringUtils.isAlpha("ab-c") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains letters, and is non-null
- * @since 3.0 Changed signature from isAlpha(String) to isAlpha(CharSequence)
- * @since 3.0 Changed "" to return false and not true
- */
- public static boolean isAlpha(CharSequence cs) {
- if (cs == null || cs.length() == 0) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (Character.isLetter(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only Unicode letters and
- * space (' ').</p>
- *
- * <p>{@code null} will return {@code false}
- * An empty CharSequence (length()=0) will return {@code true}.</p>
- *
- * <pre>
- * StringUtils.isAlphaSpace(null) = false
- * StringUtils.isAlphaSpace("") = true
- * StringUtils.isAlphaSpace(" ") = true
- * StringUtils.isAlphaSpace("abc") = true
- * StringUtils.isAlphaSpace("ab c") = true
- * StringUtils.isAlphaSpace("ab2c") = false
- * StringUtils.isAlphaSpace("ab-c") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains letters and space,
- * and is non-null
- * @since 3.0 Changed signature from isAlphaSpace(String) to isAlphaSpace(CharSequence)
- */
- public static boolean isAlphaSpace(CharSequence cs) {
- if (cs == null) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if ((Character.isLetter(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only Unicode letters or digits.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.isAlphanumeric(null) = false
- * StringUtils.isAlphanumeric("") = false
- * StringUtils.isAlphanumeric(" ") = false
- * StringUtils.isAlphanumeric("abc") = true
- * StringUtils.isAlphanumeric("ab c") = false
- * StringUtils.isAlphanumeric("ab2c") = true
- * StringUtils.isAlphanumeric("ab-c") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains letters or digits,
- * and is non-null
- * @since 3.0 Changed signature from isAlphanumeric(String) to isAlphanumeric(CharSequence)
- * @since 3.0 Changed "" to return false and not true
- */
- public static boolean isAlphanumeric(CharSequence cs) {
- if (cs == null || cs.length() == 0) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (Character.isLetterOrDigit(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only Unicode letters, digits
- * or space ({@code ' '}).</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code true}.</p>
- *
- * <pre>
- * StringUtils.isAlphanumericSpace(null) = false
- * StringUtils.isAlphanumericSpace("") = true
- * StringUtils.isAlphanumericSpace(" ") = true
- * StringUtils.isAlphanumericSpace("abc") = true
- * StringUtils.isAlphanumericSpace("ab c") = true
- * StringUtils.isAlphanumericSpace("ab2c") = true
- * StringUtils.isAlphanumericSpace("ab-c") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains letters, digits or space,
- * and is non-null
- * @since 3.0 Changed signature from isAlphanumericSpace(String) to isAlphanumericSpace(CharSequence)
- */
- public static boolean isAlphanumericSpace(CharSequence cs) {
- if (cs == null) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if ((Character.isLetterOrDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only ASCII printable characters.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code true}.</p>
- *
- * <pre>
- * StringUtils.isAsciiPrintable(null) = false
- * StringUtils.isAsciiPrintable("") = true
- * StringUtils.isAsciiPrintable(" ") = true
- * StringUtils.isAsciiPrintable("Ceki") = true
- * StringUtils.isAsciiPrintable("ab2c") = true
- * StringUtils.isAsciiPrintable("!ab-c~") = true
- * StringUtils.isAsciiPrintable("\u0020") = true
- * StringUtils.isAsciiPrintable("\u0021") = true
- * StringUtils.isAsciiPrintable("\u007e") = true
- * StringUtils.isAsciiPrintable("\u007f") = false
- * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if every character is in the range
- * 32 thru 126
- * @since 2.1
- * @since 3.0 Changed signature from isAsciiPrintable(String) to isAsciiPrintable(CharSequence)
- */
- public static boolean isAsciiPrintable(CharSequence cs) {
- if (cs == null) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (CharUtils.isAsciiPrintable(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only Unicode digits.
- * A decimal point is not a Unicode digit and returns false.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.isNumeric(null) = false
- * StringUtils.isNumeric("") = false
- * StringUtils.isNumeric(" ") = false
- * StringUtils.isNumeric("123") = true
- * StringUtils.isNumeric("12 3") = false
- * StringUtils.isNumeric("ab2c") = false
- * StringUtils.isNumeric("12-3") = false
- * StringUtils.isNumeric("12.3") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains digits, and is non-null
- * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence)
- * @since 3.0 Changed "" to return false and not true
- */
- public static boolean isNumeric(CharSequence cs) {
- if (cs == null || cs.length() == 0) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (Character.isDigit(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only Unicode digits or space
- * ({@code ' '}).
- * A decimal point is not a Unicode digit and returns false.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code true}.</p>
- *
- * <pre>
- * StringUtils.isNumericSpace(null) = false
- * StringUtils.isNumericSpace("") = true
- * StringUtils.isNumericSpace(" ") = true
- * StringUtils.isNumericSpace("123") = true
- * StringUtils.isNumericSpace("12 3") = true
- * StringUtils.isNumericSpace("ab2c") = false
- * StringUtils.isNumericSpace("12-3") = false
- * StringUtils.isNumericSpace("12.3") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains digits or space,
- * and is non-null
- * @since 3.0 Changed signature from isNumericSpace(String) to isNumericSpace(CharSequence)
- */
- public static boolean isNumericSpace(CharSequence cs) {
- if (cs == null) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if ((Character.isDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only whitespace.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code true}.</p>
- *
- * <pre>
- * StringUtils.isWhitespace(null) = false
- * StringUtils.isWhitespace("") = true
- * StringUtils.isWhitespace(" ") = true
- * StringUtils.isWhitespace("abc") = false
- * StringUtils.isWhitespace("ab2c") = false
- * StringUtils.isWhitespace("ab-c") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains whitespace, and is non-null
- * @since 2.0
- * @since 3.0 Changed signature from isWhitespace(String) to isWhitespace(CharSequence)
- */
- public static boolean isWhitespace(CharSequence cs) {
- if (cs == null) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if ((Character.isWhitespace(cs.charAt(i)) == false)) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only lowercase characters.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty CharSequence (length()=0) will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.isAllLowerCase(null) = false
- * StringUtils.isAllLowerCase("") = false
- * StringUtils.isAllLowerCase(" ") = false
- * StringUtils.isAllLowerCase("abc") = true
- * StringUtils.isAllLowerCase("abC") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains lowercase characters, and is non-null
- * @since 2.5
- * @since 3.0 Changed signature from isAllLowerCase(String) to isAllLowerCase(CharSequence)
- */
- public static boolean isAllLowerCase(CharSequence cs) {
- if (cs == null || isEmpty(cs)) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (Character.isLowerCase(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * <p>Checks if the CharSequence contains only uppercase characters.</p>
- *
- * <p>{@code null} will return {@code false}.
- * An empty String (length()=0) will return {@code false}.</p>
- *
- * <pre>
- * StringUtils.isAllUpperCase(null) = false
- * StringUtils.isAllUpperCase("") = false
- * StringUtils.isAllUpperCase(" ") = false
- * StringUtils.isAllUpperCase("ABC") = true
- * StringUtils.isAllUpperCase("aBC") = false
- * </pre>
- *
- * @param cs the CharSequence to check, may be null
- * @return {@code true} if only contains uppercase characters, and is non-null
- * @since 2.5
- * @since 3.0 Changed signature from isAllUpperCase(String) to isAllUpperCase(CharSequence)
- */
- public static boolean isAllUpperCase(CharSequence cs) {
- if (cs == null || isEmpty(cs)) {
- return false;
- }
- int sz = cs.length();
- for (int i = 0; i < sz; i++) {
- if (Character.isUpperCase(cs.charAt(i)) == false) {
- return false;
- }
- }
- return true;
- }
-
- // Defaults
- //-----------------------------------------------------------------------
- /**
- * <p>Returns either the passed in String,
- * or if the String is {@code null}, an empty String ("").</p>
- *
- * <pre>
- * StringUtils.defaultString(null) = ""
- * StringUtils.defaultString("") = ""
- * StringUtils.defaultString("bat") = "bat"
- * </pre>
- *
- * @see ObjectUtils#toString(Object)
- * @see String#valueOf(Object)
- * @param str the String to check, may be null
- * @return the passed in String, or the empty String if it
- * was {@code null}
- */
- public static String defaultString(String str) {
- return str == null ? EMPTY : str;
- }
-
- /**
- * <p>Returns either the passed in String, or if the String is
- * {@code null}, the value of {@code defaultStr}.</p>
- *
- * <pre>
- * StringUtils.defaultString(null, "NULL") = "NULL"
- * StringUtils.defaultString("", "NULL") = ""
- * StringUtils.defaultString("bat", "NULL") = "bat"
- * </pre>
- *
- * @see ObjectUtils#toString(Object,String)
- * @see String#valueOf(Object)
- * @param str the String to check, may be null
- * @param defaultStr the default String to return
- * if the input is {@code null}, may be null
- * @return the passed in String, or the default if it was {@code null}
- */
- public static String defaultString(String str, String defaultStr) {
- return str == null ? defaultStr : str;
- }
-
- /**
- * <p>Returns either the passed in CharSequence, or if the CharSequence is
- * whitespace, empty ("") or {@code null}, the value of {@code defaultStr}.</p>
- *
- * <pre>
- * StringUtils.defaultIfBlank(null, "NULL") = "NULL"
- * StringUtils.defaultIfBlank("", "NULL") = "NULL"
- * StringUtils.defaultIfBlank(" ", "NULL") = "NULL"
- * StringUtils.defaultIfBlank("bat", "NULL") = "bat"
- * StringUtils.defaultIfBlank("", null) = null
- * </pre>
- * @param <T> the specific kind of CharSequence
- * @param str the CharSequence to check, may be null
- * @param defaultStr the default CharSequence to return
- * if the input is whitespace, empty ("") or {@code null}, may be null
- * @return the passed in CharSequence, or the default
- * @see StringUtils#defaultString(String, String)
- */
- public static <T extends CharSequence> T defaultIfBlank(T str, T defaultStr) {
- return StringUtils.isBlank(str) ? defaultStr : str;
- }
-
- /**
- * <p>Returns either the passed in CharSequence, or if the CharSequence is
- * empty or {@code null}, the value of {@code defaultStr}.</p>
- *
- * <pre>
- * StringUtils.defaultIfEmpty(null, "NULL") = "NULL"
- * StringUtils.defaultIfEmpty("", "NULL") = "NULL"
- * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
- * StringUtils.defaultIfEmpty("", null) = null
- * </pre>
- * @param <T> the specific kind of CharSequence
- * @param str the CharSequence to check, may be null
- * @param defaultStr the default CharSequence to return
- * if the input is empty ("") or {@code null}, may be null
- * @return the passed in CharSequence, or the default
- * @see StringUtils#defaultString(String, String)
- */
- public static <T extends CharSequence> T defaultIfEmpty(T str, T defaultStr) {
- return StringUtils.isEmpty(str) ? defaultStr : str;
- }
-
- // Reversing
- //-----------------------------------------------------------------------
- /**
- * <p>Reverses a String as per {@link StringBuilder#reverse()}.</p>
- *
- * <p>A {@code null} String returns {@code null}.</p>
- *
- * <pre>
- * StringUtils.reverse(null) = null
- * StringUtils.reverse("") = ""
- * StringUtils.reverse("bat") = "tab"
- * </pre>
- *
- * @param str the String to reverse, may be null
- * @return the reversed String, {@code null} if null String input
- */
- public static String reverse(String str) {
- if (str == null) {
- return null;
- }
- return new StringBuilder(str).reverse().toString();
- }
-
- /**
- * <p>Reverses a String that is delimited by a specific character.</p>
- *
- * <p>The Strings between the delimiters are not reversed.
- * Thus java.lang.String becomes String.lang.java (if the delimiter
- * is {@code '.'}).</p>
- *
- * <pre>
- * StringUtils.reverseDelimited(null, *) = null
- * StringUtils.reverseDelimited("", *) = ""
- * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c"
- * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a"
- * </pre>
- *
- * @param str the String to reverse, may be null
- * @param separatorChar the separator character to use
- * @return the reversed String, {@code null} if null String input
- * @since 2.0
- */
- public static String reverseDelimited(String str, char separatorChar) {
- if (str == null) {
- return null;
- }
- // could implement manually, but simple way is to reuse other,
- // probably slower, methods.
- String[] strs = split(str, separatorChar);
- ArrayUtils.reverse(strs);
- return join(strs, separatorChar);
- }
-
- // Abbreviating
- //-----------------------------------------------------------------------
- /**
- * <p>Abbreviates a String using ellipses. This will turn
- * "Now is the time for all good men" into "Now is the time for..."</p>
- *
- * <p>Specifically:
- * <ul>
- * <li>If {@code str} is less than {@code maxWidth} characters
- * long, return it.</li>
- * <li>Else abbreviate it to {@code (substring(str, 0, max-3) + "...")}.</li>
- * <li>If {@code maxWidth} is less than {@code 4}, throw an
- * {@code IllegalArgumentException}.</li>
- * <li>In no case will it return a String of length greater than
- * {@code maxWidth}.</li>
- * </ul>
- * </p>
- *
- * <pre>
- * StringUtils.abbreviate(null, *) = null
- * StringUtils.abbreviate("", 4) = ""
- * StringUtils.abbreviate("abcdefg", 6) = "abc..."
- * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
- * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
- * StringUtils.abbreviate("abcdefg", 4) = "a..."
- * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
- * </pre>
- *
- * @param str the String to check, may be null
- * @param maxWidth maximum length of result String, must be at least 4
- * @return abbreviated String, {@code null} if null String input
- * @throws IllegalArgumentException if the width is too small
- * @since 2.0
- */
- public static String abbreviate(String str, int maxWidth) {
- return abbreviate(str, 0, maxWidth);
- }
-
- /**
- * <p>Abbreviates a String using ellipses. This will turn
- * "Now is the time for all good men" into "...is the time for..."</p>
- *
- * <p>Works like {@code abbreviate(String, int)}, but allows you to specify
- * a "left edge" offset. Note that this left edge is not necessarily going to
- * be the leftmost character in the result, or the first character following the
- * ellipses, but it will appear somewhere in the result.
- *
- * <p>In no case will it return a String of length greater than
- * {@code maxWidth}.</p>
- *
- * <pre>
- * StringUtils.abbreviate(null, *, *) = null
- * StringUtils.abbreviate("", 0, 4) = ""
- * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
- * StringUtils.abbreviate("abcdefghijklmno", 0, 10) = "abcdefg..."
- * StringUtils.abbreviate("abcdefghijklmno", 1, 10) = "abcdefg..."
- * StringUtils.abbreviate("abcdefghijklmno", 4, 10) = "abcdefg..."
- * StringUtils.abbreviate("abcdefghijklmno", 5, 10) = "...fghi..."
- * StringUtils.abbreviate("abcdefghijklmno", 6, 10) = "...ghij..."
- * StringUtils.abbreviate("abcdefghijklmno", 8, 10) = "...ijklmno"
- * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
- * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
- * StringUtils.abbreviate("abcdefghij", 0, 3) = IllegalArgumentException
- * StringUtils.abbreviate("abcdefghij", 5, 6) = IllegalArgumentException
- * </pre>
- *
- * @param str the String to check, may be null
- * @param offset left edge of source String
- * @param maxWidth maximum length of result String, must be at least 4
- * @return abbreviated String, {@code null} if null String input
- * @throws IllegalArgumentException if the width is too small
- * @since 2.0
- */
- public static String abbreviate(String str, int offset, int maxWidth) {
- if (str == null) {
- return null;
- }
- if (maxWidth < 4) {
- throw new IllegalArgumentException("Minimum abbreviation width is 4");
- }
- if (str.length() <= maxWidth) {
- return str;
- }
- if (offset > str.length()) {
- offset = str.length();
- }
- if ((str.length() - offset) < (maxWidth - 3)) {
- offset = str.length() - (maxWidth - 3);
- }
- final String abrevMarker = "...";
- if (offset <= 4) {
- return str.substring(0, maxWidth - 3) + abrevMarker;
- }
- if (maxWidth < 7) {
- throw new IllegalArgumentException("Minimum abbreviation width with offset is 7");
- }
- if ((offset + (maxWidth - 3)) < str.length()) {
- return abrevMarker + abbreviate(str.substring(offset), maxWidth - 3);
- }
- return abrevMarker + str.substring(str.length() - (maxWidth - 3));
- }
-
- /**
- * <p>Abbreviates a String to the length passed, replacing the middle characters with the supplied
- * replacement String.</p>
- *
- * <p>This abbreviation only occurs if the following criteria is met:
- * <ul>
- * <li>Neither the String for abbreviation nor the replacement String are null or empty </li>
- * <li>The length to truncate to is less than the length of the supplied String</li>
- * <li>The length to truncate to is greater than 0</li>
- * <li>The abbreviated String will have enough room for the length supplied replacement String
- * and the first and last characters of the supplied String for abbreviation</li>
- * </ul>
- * Otherwise, the returned String will be the same as the supplied String for abbreviation.
- * </p>
- *
- * <pre>
- * StringUtils.abbreviateMiddle(null, null, 0) = null
- * StringUtils.abbreviateMiddle("abc", null, 0) = "abc"
- * StringUtils.abbreviateMiddle("abc", ".", 0) = "abc"
- * StringUtils.abbreviateMiddle("abc", ".", 3) = "abc"
- * StringUtils.abbreviateMiddle("abcdef", ".", 4) = "ab.f"
- * </pre>
- *
- * @param str the String to abbreviate, may be null
- * @param middle the String to replace the middle characters with, may be null
- * @param length the length to abbreviate {@code str} to.
- * @return the abbreviated String if the above criteria is met, or the original String supplied for abbreviation.
- * @since 2.5
- */
- public static String abbreviateMiddle(String str, String middle, int length) {
- if (isEmpty(str) || isEmpty(middle)) {
- return str;
- }
-
- if (length >= str.length() || length < (middle.length()+2)) {
- return str;
- }
-
- int targetSting = length-middle.length();
- int startOffset = targetSting/2+targetSting%2;
- int endOffset = str.length()-targetSting/2;
-
- StringBuilder builder = new StringBuilder(length);
- builder.append(str.substring(0,startOffset));
- builder.append(middle);
- builder.append(str.substring(endOffset));
-
- return builder.toString();
- }
-
- // Difference
- //-----------------------------------------------------------------------
- /**
- * <p>Compares two Strings, and returns the portion where they differ.
- * (More precisely, return the remainder of the second String,
- * starting from where it's different from the first.)</p>
- *
- * <p>For example,
- * {@code difference("i am a machine", "i am a robot") -> "robot"}.</p>
- *
- * <pre>
- * StringUtils.difference(null, null) = null
- * StringUtils.difference("", "") = ""
- * StringUtils.difference("", "abc") = "abc"
- * StringUtils.difference("abc", "") = ""
- * StringUtils.difference("abc", "abc") = ""
- * StringUtils.difference("ab", "abxyz") = "xyz"
- * StringUtils.difference("abcde", "abxyz") = "xyz"
- * StringUtils.difference("abcde", "xyz") = "xyz"
- * </pre>
- *
- * @param str1 the first String, may be null
- * @param str2 the second String, may be null
- * @return the portion of str2 where it differs from str1; returns the
- * empty String if they are equal
- * @since 2.0
- */
- public static String difference(String str1, String str2) {
- if (str1 == null) {
- return str2;
- }
- if (str2 == null) {
- return str1;
- }
- int at = indexOfDifference(str1, str2);
- if (at == INDEX_NOT_FOUND) {
- return EMPTY;
- }
- return str2.substring(at);
- }
-
- /**
- * <p>Compares two CharSequences, and returns the index at which the
- * CharSequences begin to differ.</p>
- *
- * <p>For example,
- * {@code indexOfDifference("i am a machine", "i am a robot") -> 7}</p>
- *
- * <pre>
- * StringUtils.indexOfDifference(null, null) = -1
- * StringUtils.indexOfDifference("", "") = -1
- * StringUtils.indexOfDifference("", "abc") = 0
- * StringUtils.indexOfDifference("abc", "") = 0
- * StringUtils.indexOfDifference("abc", "abc") = -1
- * StringUtils.indexOfDifference("ab", "abxyz") = 2
- * StringUtils.indexOfDifference("abcde", "abxyz") = 2
- * StringUtils.indexOfDifference("abcde", "xyz") = 0
- * </pre>
- *
- * @param cs1 the first CharSequence, may be null
- * @param cs2 the second CharSequence, may be null
- * @return the index where cs1 and cs2 begin to differ; -1 if they are equal
- * @since 2.0
- * @since 3.0 Changed signature from indexOfDifference(String, String) to
- * indexOfDifference(CharSequence, CharSequence)
- */
- public static int indexOfDifference(CharSequence cs1, CharSequence cs2) {
- if (cs1 == cs2) {
- return INDEX_NOT_FOUND;
- }
- if (cs1 == null || cs2 == null) {
- return 0;
- }
- int i;
- for (i = 0; i < cs1.length() && i < cs2.length(); ++i) {
- if (cs1.charAt(i) != cs2.charAt(i)) {
- break;
- }
- }
- if (i < cs2.length() || i < cs1.length()) {
- return i;
- }
- return INDEX_NOT_FOUND;
- }
-
- /**
- * <p>Compares all CharSequences in an array and returns the index at which the
- * CharSequences begin to differ.</p>
- *
- * <p>For example,
- * <code>indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7</code></p>
- *
- * <pre>
- * StringUtils.indexOfDifference(null) = -1
- * StringUtils.indexOfDifference(new String[] {}) = -1
- * StringUtils.indexOfDifference(new String[] {"abc"}) = -1
- * StringUtils.indexOfDifference(new String[] {null, null}) = -1
- * StringUtils.indexOfDifference(new String[] {"", ""}) = -1
- * StringUtils.indexOfDifference(new String[] {"", null}) = 0
- * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0
- * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0
- * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0
- * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0
- * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1
- * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1
- * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2
- * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
- * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0
- * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0
- * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
- * </pre>
- *
- * @param css array of CharSequences, entries may be null
- * @return the index where the strings begin to differ; -1 if they are all equal
- * @since 2.4
- * @since 3.0 Changed signature from indexOfDifference(String...) to indexOfDifference(CharSequence...)
- */
- public static int indexOfDifference(CharSequence... css) {
- if (css == null || css.length <= 1) {
- return INDEX_NOT_FOUND;
- }
- boolean anyStringNull = false;
- boolean allStringsNull = true;
- int arrayLen = css.length;
- int shortestStrLen = Integer.MAX_VALUE;
- int longestStrLen = 0;
-
- // find the min and max string lengths; this avoids checking to make
- // sure we are not exceeding the length of the string each time through
- // the bottom loop.
- for (int i = 0; i < arrayLen; i++) {
- if (css[i] == null) {
- anyStringNull = true;
- shortestStrLen = 0;
- } else {
- allStringsNull = false;
- shortestStrLen = Math.min(css[i].length(), shortestStrLen);
- longestStrLen = Math.max(css[i].length(), longestStrLen);
- }
- }
-
- // handle lists containing all nulls or all empty strings
- if (allStringsNull || (longestStrLen == 0 && !anyStringNull)) {
- return INDEX_NOT_FOUND;
- }
-
- // handle lists containing some nulls or some empty strings
- if (shortestStrLen == 0) {
- return 0;
- }
-
- // find the position with the first difference across all strings
- int firstDiff = -1;
- for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) {
- char comparisonChar = css[0].charAt(stringPos);
- for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) {
- if (css[arrayPos].charAt(stringPos) != comparisonChar) {
- firstDiff = stringPos;
- break;
- }
- }
- if (firstDiff != -1) {
- break;
- }
- }
-
- if (firstDiff == -1 && shortestStrLen != longestStrLen) {
- // we compared all of the characters up to the length of the
- // shortest string and didn't find a match, but the string lengths
- // vary, so return the length of the shortest string.
- return shortestStrLen;
- }
- return firstDiff;
- }
-
- /**
- * <p>Compares all Strings in an array and returns the initial sequence of
- * characters that is common to all of them.</p>
- *
- * <p>For example,
- * <code>getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "</code></p>
- *
- * <pre>
- * StringUtils.getCommonPrefix(null) = ""
- * StringUtils.getCommonPrefix(new String[] {}) = ""
- * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
- * StringUtils.getCommonPrefix(new String[] {null, null}) = ""
- * StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
- * StringUtils.getCommonPrefix(new String[] {"", null}) = ""
- * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
- * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
- * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
- * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
- * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
- * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
- * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
- * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
- * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
- * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
- * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
- * </pre>
- *
- * @param strs array of String objects, entries may be null
- * @return the initial sequence of characters that are common to all Strings
- * in the array; empty String if the array is null, the elements are all null
- * or if there is no common prefix.
- * @since 2.4
- */
- public static String getCommonPrefix(String... strs) {
- if (strs == null || strs.length == 0) {
- return EMPTY;
- }
- int smallestIndexOfDiff = indexOfDifference(strs);
- if (smallestIndexOfDiff == INDEX_NOT_FOUND) {
- // all strings were identical
- if (strs[0] == null) {
- return EMPTY;
- }
- return strs[0];
- } else if (smallestIndexOfDiff == 0) {
- // there were no common initial characters
- return EMPTY;
- } else {
- // we found a common initial character sequence
- return strs[0].substring(0, smallestIndexOfDiff);
- }
- }
-
- // Misc
- //-----------------------------------------------------------------------
- /**
- * <p>Find the Levenshtein distance between two Strings.</p>
- *
- * <p>This is the number of changes needed to change one String into
- * another, where each change is a single character modification (deletion,
- * insertion or substitution).</p>
- *
- * <p>The previous implementation of the Levenshtein distance algorithm
- * was from <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p>
- *
- * <p>Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError
- * which can occur when my Java implementation is used with very large strings.<br>
- * This implementation of the Levenshtein distance algorithm
- * is from <a href="http://www.merriampark.com/ldjava.htm">http://www.merriampark.com/ldjava.htm</a></p>
- *
- * <pre>
- * StringUtils.getLevenshteinDistance(null, *) = IllegalArgumentException
- * StringUtils.getLevenshteinDistance(*, null) = IllegalArgumentException
- * StringUtils.getLevenshteinDistance("","") = 0
- * StringUtils.getLevenshteinDistance("","a") = 1
- * StringUtils.getLevenshteinDistance("aaapppp", "") = 7
- * StringUtils.getLevenshteinDistance("frog", "fog") = 1
- * StringUtils.getLevenshteinDistance("fly", "ant") = 3
- * StringUtils.getLevenshteinDistance("elephant", "hippo") = 7
- * StringUtils.getLevenshteinDistance("hippo", "elephant") = 7
- * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8
- * StringUtils.getLevenshteinDistance("hello", "hallo") = 1
- * </pre>
- *
- * @param s the first String, must not be null
- * @param t the second String, must not be null
- * @return result distance
- * @throws IllegalArgumentException if either String input {@code null}
- * @since 3.0 Changed signature from getLevenshteinDistance(String, String) to
- * getLevenshteinDistance(CharSequence, CharSequence)
- */
- public static int getLevenshteinDistance(CharSequence s, CharSequence t) {
- if (s == null || t == null) {
- throw new IllegalArgumentException("Strings must not be null");
- }
-
- /*
- The difference between this impl. and the previous is that, rather
- than creating and retaining a matrix of size s.length() + 1 by t.length() + 1,
- we maintain two single-dimensional arrays of length s.length() + 1. The first, d,
- is the 'current working' distance array that maintains the newest distance cost
- counts as we iterate through the characters of String s. Each time we increment
- the index of String t we are comparing, d is copied to p, the second int[]. Doing so
- allows us to retain the previous cost counts as required by the algorithm (taking
- the minimum of the cost count to the left, up one, and diagonally up and to the left
- of the current cost count being calculated). (Note that the arrays aren't really
- copied anymore, just switched...this is clearly much better than cloning an array
- or doing a System.arraycopy() each time through the outer loop.)
-
- Effectively, the difference between the two implementations is this one does not
- cause an out of memory condition when calculating the LD over two very large strings.
- */
-
- int n = s.length(); // length of s
- int m = t.length(); // length of t
-
- if (n == 0) {
- return m;
- } else if (m == 0) {
- return n;
- }
-
- if (n > m) {
- // swap the input strings to consume less memory
- CharSequence tmp = s;
- s = t;
- t = tmp;
- n = m;
- m = t.length();
- }
-
- int p[] = new int[n + 1]; //'previous' cost array, horizontally
- int d[] = new int[n + 1]; // cost array, horizontally
- int _d[]; //placeholder to assist in swapping p and d
-
- // indexes into strings s and t
- int i; // iterates through s
- int j; // iterates through t
-
- char t_j; // jth character of t
-
- int cost; // cost
-
- for (i = 0; i <= n; i++) {
- p[i] = i;
- }
-
- for (j = 1; j <= m; j++) {
- t_j = t.charAt(j - 1);
- d[0] = j;
-
- for (i = 1; i <= n; i++) {
- cost = s.charAt(i - 1) == t_j ? 0 : 1;
- // minimum of cell to the left+1, to the top+1, diagonally left and up +cost
- d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost);
- }
-
- // copy current distance counts to 'previous row' distance counts
- _d = p;
- p = d;
- d = _d;
- }
-
- // our last action in the above loop was to switch d and p, so p now
- // actually has the most recent cost counts
- return p[n];
- }
-
- /**
- * <p>Find the Levenshtein distance between two Strings if it's less than or equal to a given
- * threshold.</p>
- *
- * <p>This is the number of changes needed to change one String into
- * another, where each change is a single character modification (deletion,
- * insertion or substitution).</p>
- *
- * <p>This implementation follows from Algorithms on Strings, Trees and Sequences by Dan Gusfield
- * and Chas Emerick's implementation of the Levenshtein distance algorithm from
- * <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p>
- *
- * <pre>
- * StringUtils.getLevenshteinDistance(null, *, *) = IllegalArgumentException
- * StringUtils.getLevenshteinDistance(*, null, *) = IllegalArgumentException
- * StringUtils.getLevenshteinDistance(*, *, -1) = IllegalArgumentException
- * StringUtils.getLevenshteinDistance("","", 0) = 0
- * StringUtils.getLevenshteinDistance("aaapppp", "", 8) = 7
- * StringUtils.getLevenshteinDistance("aaapppp", "", 7) = 7
- * StringUtils.getLevenshteinDistance("aaapppp", "", 6)) = -1
- * StringUtils.getLevenshteinDistance("elephant", "hippo", 7) = 7
- * StringUtils.getLevenshteinDistance("elephant", "hippo", 6) = -1
- * StringUtils.getLevenshteinDistance("hippo", "elephant", 7) = 7
- * StringUtils.getLevenshteinDistance("hippo", "elephant", 6) = -1
- * </pre>
- *
- * @param s the first String, must not be null
- * @param t the second String, must not be null
- * @param threshold the target threshold, must not be negative
- * @return result distance, or {@code -1} if the distance would be greater than the threshold
- * @throws IllegalArgumentException if either String input {@code null} or negative threshold
- */
- public static int getLevenshteinDistance(CharSequence s, CharSequence t, int threshold) {
- if (s == null || t == null) {
- throw new IllegalArgumentException("Strings must not be null");
- }
- if (threshold < 0) {
- throw new IllegalArgumentException("Threshold must not be negative");
- }
-
- /*
- This implementation only computes the distance if it's less than or equal to the
- threshold value, returning -1 if it's greater. The advantage is performance: unbounded
- distance is O(nm), but a bound of k allows us to reduce it to O(km) time by only
- computing a diagonal stripe of width 2k + 1 of the cost table.
- It is also possible to use this to compute the unbounded Levenshtein distance by starting
- the threshold at 1 and doubling each time until the distance is found; this is O(dm), where
- d is the distance.
-
- One subtlety comes from needing to ignore entries on the border of our stripe
- eg.
- p[] = |#|#|#|*
- d[] = *|#|#|#|
- We must ignore the entry to the left of the leftmost member
- We must ignore the entry above the rightmost member
-
- Another subtlety comes from our stripe running off the matrix if the strings aren't
- of the same size. Since string s is always swapped to be the shorter of the two,
- the stripe will always run off to the upper right instead of the lower left of the matrix.
-
- As a concrete example, suppose s is of length 5, t is of length 7, and our threshold is 1.
- In this case we're going to walk a stripe of length 3. The matrix would look like so:
-
- 1 2 3 4 5
- 1 |#|#| | | |
- 2 |#|#|#| | |
- 3 | |#|#|#| |
- 4 | | |#|#|#|
- 5 | | | |#|#|
- 6 | | | | |#|
- 7 | | | | | |
-
- Note how the stripe leads off the table as there is no possible way to turn a string of length 5
- into one of length 7 in edit distance of 1.
-
- Additionally, this implementation decreases memory usage by using two
- single-dimensional arrays and swapping them back and forth instead of allocating
- an entire n by m matrix. This requires a few minor changes, such as immediately returning
- when it's detected that the stripe has run off the matrix and initially filling the arrays with
- large values so that entries we don't compute are ignored.
-
- See Algorithms on Strings, Trees and Sequences by Dan Gusfield for some discussion.
- */
-
- int n = s.length(); // length of s
- int m = t.length(); // length of t
-
- // if one string is empty, the edit distance is necessarily the length of the other
- if (n == 0) {
- return m <= threshold ? m : -1;
- } else if (m == 0) {
- return n <= threshold ? n : -1;
- }
-
- if (n > m) {
- // swap the two strings to consume less memory
- CharSequence tmp = s;
- s = t;
- t = tmp;
- n = m;
- m = t.length();
- }
-
- int p[] = new int[n + 1]; // 'previous' cost array, horizontally
- int d[] = new int[n + 1]; // cost array, horizontally
- int _d[]; // placeholder to assist in swapping p and d
-
- // fill in starting table values
- int boundary = Math.min(n, threshold) + 1;
- for (int i = 0; i < boundary; i++) {
- p[i] = i;
- }
- // these fills ensure that the value above the rightmost entry of our
- // stripe will be ignored in following loop iterations
- Arrays.fill(p, boundary, p.length, Integer.MAX_VALUE);
- Arrays.fill(d, Integer.MAX_VALUE);
-
- // iterates through t
- for (int j = 1; j <= m; j++) {
- char t_j = t.charAt(j - 1); // jth character of t
- d[0] = j;
-
- // compute stripe indices, constrain to array size
- int min = Math.max(1, j - threshold);
- int max = Math.min(n, j + threshold);
-
- // the stripe may lead off of the table if s and t are of different sizes
- if (min > max) {
- return -1;
- }
-
- // ignore entry left of leftmost
- if (min > 1) {
- d[min - 1] = Integer.MAX_VALUE;
- }
-
- // iterates through [min, max] in s
- for (int i = min; i <= max; i++) {
- if (s.charAt(i - 1) == t_j) {
- // diagonally left and up
- d[i] = p[i - 1];
- } else {
- // 1 + minimum of cell to the left, to the top, diagonally left and up
- d[i] = 1 + Math.min(Math.min(d[i - 1], p[i]), p[i - 1]);
- }
- }
-
- // copy current distance counts to 'previous row' distance counts
- _d = p;
- p = d;
- d = _d;
- }
-
- // if p[n] is greater than the threshold, there's no guarantee on it being the correct
- // distance
- if (p[n] <= threshold) {
- return p[n];
- } else {
- return -1;
- }
- }
-
- // startsWith
- //-----------------------------------------------------------------------
-
- /**
- * <p>Check if a CharSequence starts with a specified prefix.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered to be equal. The comparison is case sensitive.</p>
- *
- * <pre>
- * StringUtils.startsWith(null, null) = true
- * StringUtils.startsWith(null, "abc") = false
- * StringUtils.startsWith("abcdef", null) = false
- * StringUtils.startsWith("abcdef", "abc") = true
- * StringUtils.startsWith("ABCDEF", "abc") = false
- * </pre>
- *
- * @see java.lang.String#startsWith(String)
- * @param str the CharSequence to check, may be null
- * @param prefix the prefix to find, may be null
- * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or
- * both {@code null}
- * @since 2.4
- * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence)
- */
- public static boolean startsWith(CharSequence str, CharSequence prefix) {
- return startsWith(str, prefix, false);
- }
-
- /**
- * <p>Case insensitive check if a CharSequence starts with a specified prefix.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered to be equal. The comparison is case insensitive.</p>
- *
- * <pre>
- * StringUtils.startsWithIgnoreCase(null, null) = true
- * StringUtils.startsWithIgnoreCase(null, "abc") = false
- * StringUtils.startsWithIgnoreCase("abcdef", null) = false
- * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
- * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
- * </pre>
- *
- * @see java.lang.String#startsWith(String)
- * @param str the CharSequence to check, may be null
- * @param prefix the prefix to find, may be null
- * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or
- * both {@code null}
- * @since 2.4
- * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence)
- */
- public static boolean startsWithIgnoreCase(CharSequence str, CharSequence prefix) {
- return startsWith(str, prefix, true);
- }
-
- /**
- * <p>Check if a CharSequence starts with a specified prefix (optionally case insensitive).</p>
- *
- * @see java.lang.String#startsWith(String)
- * @param str the CharSequence to check, may be null
- * @param prefix the prefix to find, may be null
- * @param ignoreCase indicates whether the compare should ignore case
- * (case insensitive) or not.
- * @return {@code true} if the CharSequence starts with the prefix or
- * both {@code null}
- */
- private static boolean startsWith(CharSequence str, CharSequence prefix, boolean ignoreCase) {
- if (str == null || prefix == null) {
- return (str == null && prefix == null);
- }
- if (prefix.length() > str.length()) {
- return false;
- }
- return CharSequenceUtils.regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length());
- }
-
- /**
- * <p>Check if a CharSequence starts with any of an array of specified strings.</p>
- *
- * <pre>
- * StringUtils.startsWithAny(null, null) = false
- * StringUtils.startsWithAny(null, new String[] {"abc"}) = false
- * StringUtils.startsWithAny("abcxyz", null) = false
- * StringUtils.startsWithAny("abcxyz", new String[] {""}) = false
- * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true
- * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
- * </pre>
- *
- * @param string the CharSequence to check, may be null
- * @param searchStrings the CharSequences to find, may be null or empty
- * @return {@code true} if the CharSequence starts with any of the the prefixes, case insensitive, or
- * both {@code null}
- * @since 2.5
- * @since 3.0 Changed signature from startsWithAny(String, String[]) to startsWithAny(CharSequence, CharSequence...)
- */
- public static boolean startsWithAny(CharSequence string, CharSequence... searchStrings) {
- if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) {
- return false;
- }
- for (CharSequence searchString : searchStrings) {
- if (StringUtils.startsWith(string, searchString)) {
- return true;
- }
- }
- return false;
- }
-
- // endsWith
- //-----------------------------------------------------------------------
-
- /**
- * <p>Check if a CharSequence ends with a specified suffix.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered to be equal. The comparison is case sensitive.</p>
- *
- * <pre>
- * StringUtils.endsWith(null, null) = true
- * StringUtils.endsWith(null, "def") = false
- * StringUtils.endsWith("abcdef", null) = false
- * StringUtils.endsWith("abcdef", "def") = true
- * StringUtils.endsWith("ABCDEF", "def") = false
- * StringUtils.endsWith("ABCDEF", "cde") = false
- * </pre>
- *
- * @see java.lang.String#endsWith(String)
- * @param str the CharSequence to check, may be null
- * @param suffix the suffix to find, may be null
- * @return {@code true} if the CharSequence ends with the suffix, case sensitive, or
- * both {@code null}
- * @since 2.4
- * @since 3.0 Changed signature from endsWith(String, String) to endsWith(CharSequence, CharSequence)
- */
- public static boolean endsWith(CharSequence str, CharSequence suffix) {
- return endsWith(str, suffix, false);
- }
-
- /**
- * <p>Case insensitive check if a CharSequence ends with a specified suffix.</p>
- *
- * <p>{@code null}s are handled without exceptions. Two {@code null}
- * references are considered to be equal. The comparison is case insensitive.</p>
- *
- * <pre>
- * StringUtils.endsWithIgnoreCase(null, null) = true
- * StringUtils.endsWithIgnoreCase(null, "def") = false
- * StringUtils.endsWithIgnoreCase("abcdef", null) = false
- * StringUtils.endsWithIgnoreCase("abcdef", "def") = true
- * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true
- * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false
- * </pre>
- *
- * @see java.lang.String#endsWith(String)
- * @param str the CharSequence to check, may be null
- * @param suffix the suffix to find, may be null
- * @return {@code true} if the CharSequence ends with the suffix, case insensitive, or
- * both {@code null}
- * @since 2.4
- * @since 3.0 Changed signature from endsWithIgnoreCase(String, String) to endsWithIgnoreCase(CharSequence, CharSequence)
- */
- public static boolean endsWithIgnoreCase(CharSequence str, CharSequence suffix) {
- return endsWith(str, suffix, true);
- }
-
- /**
- * <p>Check if a CharSequence ends with a specified suffix (optionally case insensitive).</p>
- *
- * @see java.lang.String#endsWith(String)
- * @param str the CharSequence to check, may be null
- * @param suffix the suffix to find, may be null
- * @param ignoreCase indicates whether the compare should ignore case
- * (case insensitive) or not.
- * @return {@code true} if the CharSequence starts with the prefix or
- * both {@code null}
- */
- private static boolean endsWith(CharSequence str, CharSequence suffix, boolean ignoreCase) {
- if (str == null || suffix == null) {
- return str == null && suffix == null;
- }
- if (suffix.length() > str.length()) {
- return false;
- }
- int strOffset = str.length() - suffix.length();
- return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length());
- }
-
- /**
- * <p>
- * Similar to <a
- * href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize
- * -space</a>
- * </p>
- * <p>
- * The function returns the argument string with whitespace normalized by using
- * <code>{@link #trim(String)}</code> to remove leading and trailing whitespace
- * and then replacing sequences of whitespace characters by a single space.
- * </p>
- * In XML Whitespace characters are the same as those allowed by the <a
- * href="http://www.w3.org/TR/REC-xml/#NT-S">S</a> production, which is S ::= (#x20 | #x9 | #xD | #xA)+
- * <p>
- * Java's regexp pattern \s defines whitespace as [ \t\n\x0B\f\r]
- * <p>
- * For reference:
- * <ul>
- * <li>\x0B = vertical tab</li>
- * <li>\f = #xC = form feed</li>
- * <li>#x20 = space</li>
- * <li>#x9 = \t</li>
- * <li>#xA = \n</li>
- * <li>#xD = \r</li>
- * </ul>
- * </p>
- * <p>
- * The difference is that Java's whitespace includes vertical tab and form feed, which this functional will also
- * normalize. Additionally <code>{@link #trim(String)}</code> removes control characters (char <= 32) from both
- * ends of this String.
- * </p>
- *
- * @see Pattern
- * @see #trim(String)
- * @see <a
- * href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize-space</a>
- * @param str the source String to normalize whitespaces from, may be null
- * @return the modified string with whitespace normalized, {@code null} if null String input
- *
- * @since 3.0
- */
- public static String normalizeSpace(String str) {
- if (str == null) {
- return null;
- }
- return WHITESPACE_BLOCK.matcher(trim(str)).replaceAll(" ");
- }
-
- /**
- * <p>Check if a CharSequence ends with any of an array of specified strings.</p>
- *
- * <pre>
- * StringUtils.endsWithAny(null, null) = false
- * StringUtils.endsWithAny(null, new String[] {"abc"}) = false
- * StringUtils.endsWithAny("abcxyz", null) = false
- * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true
- * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true
- * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
- * </pre>
- *
- * @param string the CharSequence to check, may be null
- * @param searchStrings the CharSequences to find, may be null or empty
- * @return {@code true} if the CharSequence ends with any of the the prefixes, case insensitive, or
- * both {@code null}
- * @since 3.0
- */
- public static boolean endsWithAny(CharSequence string, CharSequence... searchStrings) {
- if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) {
- return false;
- }
- for (CharSequence searchString : searchStrings) {
- if (StringUtils.endsWith(string, searchString)) {
- return true;
- }
- }
- return false;
- }
-
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +/** + * <p>Operations on {@link java.lang.String} that are + * {@code null} safe.</p> + * + * <ul> + * <li><b>IsEmpty/IsBlank</b> + * - checks if a String contains text</li> + * <li><b>Trim/Strip</b> + * - removes leading and trailing whitespace</li> + * <li><b>Equals</b> + * - compares two strings null-safe</li> + * <li><b>startsWith</b> + * - check if a String starts with a prefix null-safe</li> + * <li><b>endsWith</b> + * - check if a String ends with a suffix null-safe</li> + * <li><b>IndexOf/LastIndexOf/Contains</b> + * - null-safe index-of checks + * <li><b>IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut</b> + * - index-of any of a set of Strings</li> + * <li><b>ContainsOnly/ContainsNone/ContainsAny</b> + * - does String contains only/none/any of these characters</li> + * <li><b>Substring/Left/Right/Mid</b> + * - null-safe substring extractions</li> + * <li><b>SubstringBefore/SubstringAfter/SubstringBetween</b> + * - substring extraction relative to other strings</li> + * <li><b>Split/Join</b> + * - splits a String into an array of substrings and vice versa</li> + * <li><b>Remove/Delete</b> + * - removes part of a String</li> + * <li><b>Replace/Overlay</b> + * - Searches a String and replaces one String with another</li> + * <li><b>Chomp/Chop</b> + * - removes the last part of a String</li> + * <li><b>LeftPad/RightPad/Center/Repeat</b> + * - pads a String</li> + * <li><b>UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize</b> + * - changes the case of a String</li> + * <li><b>CountMatches</b> + * - counts the number of occurrences of one String in another</li> + * <li><b>IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable</b> + * - checks the characters in a String</li> + * <li><b>DefaultString</b> + * - protects against a null input String</li> + * <li><b>Reverse/ReverseDelimited</b> + * - reverses a String</li> + * <li><b>Abbreviate</b> + * - abbreviates a string using ellipsis</li> + * <li><b>Difference</b> + * - compares Strings and reports on their differences</li> + * <li><b>LevenshteinDistance</b> + * - the number of changes needed to change one String into another</li> + * </ul> + * + * <p>The {@code StringUtils} class defines certain words related to + * String handling.</p> + * + * <ul> + * <li>null - {@code null}</li> + * <li>empty - a zero-length string ({@code ""})</li> + * <li>space - the space character ({@code ' '}, char 32)</li> + * <li>whitespace - the characters defined by {@link Character#isWhitespace(char)}</li> + * <li>trim - the characters <= 32 as in {@link String#trim()}</li> + * </ul> + * + * <p>{@code StringUtils} handles {@code null} input Strings quietly. + * That is to say that a {@code null} input will return {@code null}. + * Where a {@code boolean} or {@code int} is being returned + * details vary by method.</p> + * + * <p>A side effect of the {@code null} handling is that a + * {@code NullPointerException} should be considered a bug in + * {@code StringUtils}.</p> + * + * <p>Methods in this class give sample code to explain their operation. + * The symbol {@code *} is used to indicate any input including {@code null}.</p> + * + * <p>#ThreadSafe#</p> + * @see java.lang.String + * @since 1.0 + * @version $Id$ + */ +//@Immutable +public class StringUtils { + // Performance testing notes (JDK 1.4, Jul03, scolebourne) + // Whitespace: + // Character.isWhitespace() is faster than WHITESPACE.indexOf() + // where WHITESPACE is a string of all whitespace characters + // + // Character access: + // String.charAt(n) versus toCharArray(), then array[n] + // String.charAt(n) is about 15% worse for a 10K string + // They are about equal for a length 50 string + // String.charAt(n) is about 4 times better for a length 3 string + // String.charAt(n) is best bet overall + // + // Append: + // String.concat about twice as fast as StringBuffer.append + // (not sure who tested this) + + /** + * The empty String {@code ""}. + * @since 2.0 + */ + public static final String EMPTY = ""; + + /** + * Represents a failed index search. + * @since 2.1 + */ + public static final int INDEX_NOT_FOUND = -1; + + /** + * <p>The maximum size to which the padding constant(s) can expand.</p> + */ + private static final int PAD_LIMIT = 8192; + + /** + * A regex pattern for recognizing blocks of whitespace characters. + */ + private static final Pattern WHITESPACE_BLOCK = Pattern.compile("\\s+"); + + /** + * <p>{@code StringUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * {@code StringUtils.trim(" foo ");}.</p> + * + * <p>This constructor is public to permit tools that require a JavaBean + * instance to operate.</p> + */ + public StringUtils() { + super(); + } + + // Empty checks + //----------------------------------------------------------------------- + /** + * <p>Checks if a CharSequence is empty ("") or null.</p> + * + * <pre> + * StringUtils.isEmpty(null) = true + * StringUtils.isEmpty("") = true + * StringUtils.isEmpty(" ") = false + * StringUtils.isEmpty("bob") = false + * StringUtils.isEmpty(" bob ") = false + * </pre> + * + * <p>NOTE: This method changed in Lang version 2.0. + * It no longer trims the CharSequence. + * That functionality is available in isBlank().</p> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is empty or null + * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) + */ + public static boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + /** + * <p>Checks if a CharSequence is not empty ("") and not null.</p> + * + * <pre> + * StringUtils.isNotEmpty(null) = false + * StringUtils.isNotEmpty("") = false + * StringUtils.isNotEmpty(" ") = true + * StringUtils.isNotEmpty("bob") = true + * StringUtils.isNotEmpty(" bob ") = true + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null + * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) + */ + public static boolean isNotEmpty(CharSequence cs) { + return !StringUtils.isEmpty(cs); + } + + /** + * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p> + * + * <pre> + * StringUtils.isBlank(null) = true + * StringUtils.isBlank("") = true + * StringUtils.isBlank(" ") = true + * StringUtils.isBlank("bob") = false + * StringUtils.isBlank(" bob ") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is null, empty or whitespace + * @since 2.0 + * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) + */ + public static boolean isBlank(CharSequence cs) { + int strLen; + if (cs == null || (strLen = cs.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if ((Character.isWhitespace(cs.charAt(i)) == false)) { + return false; + } + } + return true; + } + + /** + * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p> + * + * <pre> + * StringUtils.isNotBlank(null) = false + * StringUtils.isNotBlank("") = false + * StringUtils.isNotBlank(" ") = false + * StringUtils.isNotBlank("bob") = true + * StringUtils.isNotBlank(" bob ") = true + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is + * not empty and not null and not whitespace + * @since 2.0 + * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) + */ + public static boolean isNotBlank(CharSequence cs) { + return !StringUtils.isBlank(cs); + } + + // Trim + //----------------------------------------------------------------------- + /** + * <p>Removes control characters (char <= 32) from both + * ends of this String, handling {@code null} by returning + * {@code null}.</p> + * + * <p>The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #strip(String)}.</p> + * + * <p>To trim your choice of characters, use the + * {@link #strip(String, String)} methods.</p> + * + * <pre> + * StringUtils.trim(null) = null + * StringUtils.trim("") = "" + * StringUtils.trim(" ") = "" + * StringUtils.trim("abc") = "abc" + * StringUtils.trim(" abc ") = "abc" + * </pre> + * + * @param str the String to be trimmed, may be null + * @return the trimmed string, {@code null} if null String input + */ + public static String trim(String str) { + return str == null ? null : str.trim(); + } + + /** + * <p>Removes control characters (char <= 32) from both + * ends of this String returning {@code null} if the String is + * empty ("") after the trim or if it is {@code null}. + * + * <p>The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #stripToNull(String)}.</p> + * + * <pre> + * StringUtils.trimToNull(null) = null + * StringUtils.trimToNull("") = null + * StringUtils.trimToNull(" ") = null + * StringUtils.trimToNull("abc") = "abc" + * StringUtils.trimToNull(" abc ") = "abc" + * </pre> + * + * @param str the String to be trimmed, may be null + * @return the trimmed String, + * {@code null} if only chars <= 32, empty or null String input + * @since 2.0 + */ + public static String trimToNull(String str) { + String ts = trim(str); + return isEmpty(ts) ? null : ts; + } + + /** + * <p>Removes control characters (char <= 32) from both + * ends of this String returning an empty String ("") if the String + * is empty ("") after the trim or if it is {@code null}. + * + * <p>The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #stripToEmpty(String)}.</p> + * + * <pre> + * StringUtils.trimToEmpty(null) = "" + * StringUtils.trimToEmpty("") = "" + * StringUtils.trimToEmpty(" ") = "" + * StringUtils.trimToEmpty("abc") = "abc" + * StringUtils.trimToEmpty(" abc ") = "abc" + * </pre> + * + * @param str the String to be trimmed, may be null + * @return the trimmed String, or an empty String if {@code null} input + * @since 2.0 + */ + public static String trimToEmpty(String str) { + return str == null ? EMPTY : str.trim(); + } + + // Stripping + //----------------------------------------------------------------------- + /** + * <p>Strips whitespace from the start and end of a String.</p> + * + * <p>This is similar to {@link #trim(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.strip(null) = null + * StringUtils.strip("") = "" + * StringUtils.strip(" ") = "" + * StringUtils.strip("abc") = "abc" + * StringUtils.strip(" abc") = "abc" + * StringUtils.strip("abc ") = "abc" + * StringUtils.strip(" abc ") = "abc" + * StringUtils.strip(" ab c ") = "ab c" + * </pre> + * + * @param str the String to remove whitespace from, may be null + * @return the stripped String, {@code null} if null String input + */ + public static String strip(String str) { + return strip(str, null); + } + + /** + * <p>Strips whitespace from the start and end of a String returning + * {@code null} if the String is empty ("") after the strip.</p> + * + * <p>This is similar to {@link #trimToNull(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.stripToNull(null) = null + * StringUtils.stripToNull("") = null + * StringUtils.stripToNull(" ") = null + * StringUtils.stripToNull("abc") = "abc" + * StringUtils.stripToNull(" abc") = "abc" + * StringUtils.stripToNull("abc ") = "abc" + * StringUtils.stripToNull(" abc ") = "abc" + * StringUtils.stripToNull(" ab c ") = "ab c" + * </pre> + * + * @param str the String to be stripped, may be null + * @return the stripped String, + * {@code null} if whitespace, empty or null String input + * @since 2.0 + */ + public static String stripToNull(String str) { + if (str == null) { + return null; + } + str = strip(str, null); + return str.length() == 0 ? null : str; + } + + /** + * <p>Strips whitespace from the start and end of a String returning + * an empty String if {@code null} input.</p> + * + * <p>This is similar to {@link #trimToEmpty(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.stripToEmpty(null) = "" + * StringUtils.stripToEmpty("") = "" + * StringUtils.stripToEmpty(" ") = "" + * StringUtils.stripToEmpty("abc") = "abc" + * StringUtils.stripToEmpty(" abc") = "abc" + * StringUtils.stripToEmpty("abc ") = "abc" + * StringUtils.stripToEmpty(" abc ") = "abc" + * StringUtils.stripToEmpty(" ab c ") = "ab c" + * </pre> + * + * @param str the String to be stripped, may be null + * @return the trimmed String, or an empty String if {@code null} input + * @since 2.0 + */ + public static String stripToEmpty(String str) { + return str == null ? EMPTY : strip(str, null); + } + + /** + * <p>Strips any of a set of characters from the start and end of a String. + * This is similar to {@link String#trim()} but allows the characters + * to be stripped to be controlled.</p> + * + * <p>A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.</p> + * + * <p>If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}. + * Alternatively use {@link #strip(String)}.</p> + * + * <pre> + * StringUtils.strip(null, *) = null + * StringUtils.strip("", *) = "" + * StringUtils.strip("abc", null) = "abc" + * StringUtils.strip(" abc", null) = "abc" + * StringUtils.strip("abc ", null) = "abc" + * StringUtils.strip(" abc ", null) = "abc" + * StringUtils.strip(" abcyx", "xyz") = " abc" + * </pre> + * + * @param str the String to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String strip(String str, String stripChars) { + if (isEmpty(str)) { + return str; + } + str = stripStart(str, stripChars); + return stripEnd(str, stripChars); + } + + /** + * <p>Strips any of a set of characters from the start of a String.</p> + * + * <p>A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.</p> + * + * <p>If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.stripStart(null, *) = null + * StringUtils.stripStart("", *) = "" + * StringUtils.stripStart("abc", "") = "abc" + * StringUtils.stripStart("abc", null) = "abc" + * StringUtils.stripStart(" abc", null) = "abc" + * StringUtils.stripStart("abc ", null) = "abc " + * StringUtils.stripStart(" abc ", null) = "abc " + * StringUtils.stripStart("yxabc ", "xyz") = "abc " + * </pre> + * + * @param str the String to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String stripStart(String str, String stripChars) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + int start = 0; + if (stripChars == null) { + while ((start != strLen) && Character.isWhitespace(str.charAt(start))) { + start++; + } + } else if (stripChars.length() == 0) { + return str; + } else { + while ((start != strLen) && (stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND)) { + start++; + } + } + return str.substring(start); + } + + /** + * <p>Strips any of a set of characters from the end of a String.</p> + * + * <p>A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.</p> + * + * <p>If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.stripEnd(null, *) = null + * StringUtils.stripEnd("", *) = "" + * StringUtils.stripEnd("abc", "") = "abc" + * StringUtils.stripEnd("abc", null) = "abc" + * StringUtils.stripEnd(" abc", null) = " abc" + * StringUtils.stripEnd("abc ", null) = "abc" + * StringUtils.stripEnd(" abc ", null) = " abc" + * StringUtils.stripEnd(" abcyx", "xyz") = " abc" + * StringUtils.stripEnd("120.00", ".0") = "12" + * </pre> + * + * @param str the String to remove characters from, may be null + * @param stripChars the set of characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String stripEnd(String str, String stripChars) { + int end; + if (str == null || (end = str.length()) == 0) { + return str; + } + + if (stripChars == null) { + while ((end != 0) && Character.isWhitespace(str.charAt(end - 1))) { + end--; + } + } else if (stripChars.length() == 0) { + return str; + } else { + while ((end != 0) && (stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND)) { + end--; + } + } + return str.substring(0, end); + } + + // StripAll + //----------------------------------------------------------------------- + /** + * <p>Strips whitespace from the start and end of every String in an array. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <p>A new array is returned each time, except for length zero. + * A {@code null} array will return {@code null}. + * An empty array will return itself. + * A {@code null} array entry will be ignored.</p> + * + * <pre> + * StringUtils.stripAll(null) = null + * StringUtils.stripAll([]) = [] + * StringUtils.stripAll(["abc", " abc"]) = ["abc", "abc"] + * StringUtils.stripAll(["abc ", null]) = ["abc", null] + * </pre> + * + * @param strs the array to remove whitespace from, may be null + * @return the stripped Strings, {@code null} if null array input + */ + public static String[] stripAll(String... strs) { + return stripAll(strs, null); + } + + /** + * <p>Strips any of a set of characters from the start and end of every + * String in an array.</p> + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <p>A new array is returned each time, except for length zero. + * A {@code null} array will return {@code null}. + * An empty array will return itself. + * A {@code null} array entry will be ignored. + * A {@code null} stripChars will strip whitespace as defined by + * {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.stripAll(null, *) = null + * StringUtils.stripAll([], *) = [] + * StringUtils.stripAll(["abc", " abc"], null) = ["abc", "abc"] + * StringUtils.stripAll(["abc ", null], null) = ["abc", null] + * StringUtils.stripAll(["abc ", null], "yz") = ["abc ", null] + * StringUtils.stripAll(["yabcz", null], "yz") = ["abc", null] + * </pre> + * + * @param strs the array to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped Strings, {@code null} if null array input + */ + public static String[] stripAll(String[] strs, String stripChars) { + int strsLen; + if (strs == null || (strsLen = strs.length) == 0) { + return strs; + } + String[] newArr = new String[strsLen]; + for (int i = 0; i < strsLen; i++) { + newArr[i] = strip(strs[i], stripChars); + } + return newArr; + } + + /** + * <p>Removes diacritics (~= accents) from a string. The case will not be altered.</p> + * <p>For instance, 'à' will be replaced by 'a'.</p> + * <p>Note that ligatures will be left as is.</p> + * + * <p>This method will use the first available implementation of: + * Java 6's {@link java.text.Normalizer}, Java 1.3–1.5's {@code sun.text.Normalizer}</p> + * + * <pre> + * StringUtils.stripAccents(null) = null + * StringUtils.stripAccents("") = "" + * StringUtils.stripAccents("control") = "control" + * StringUtils.stripAccents("éclair") = "eclair" + * </pre> + * + * @param input String to be stripped + * @return input text with diacritics removed + * + * @since 3.0 + */ + // See also Lucene's ASCIIFoldingFilter (Lucene 2.9) that replaces accented characters by their unaccented equivalent (and uncommitted bug fix: https://issues.apache.org/jira/browse/LUCENE-1343?focusedCommentId=12858907&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12858907). + public static String stripAccents(String input) { + if(input == null) { + return null; + } + try { + String result = null; + if (java6Available) { + result = removeAccentsJava6(input); + } else if (sunAvailable) { + result = removeAccentsSUN(input); + } else { + throw new UnsupportedOperationException( + "The stripAccents(CharSequence) method requires at least Java 1.6 or a Sun JVM"); + } + // Note that none of the above methods correctly remove ligatures... + return result; + } catch(IllegalArgumentException iae) { + throw new RuntimeException("IllegalArgumentException occurred", iae); + } catch(IllegalAccessException iae) { + throw new RuntimeException("IllegalAccessException occurred", iae); + } catch(InvocationTargetException ite) { + throw new RuntimeException("InvocationTargetException occurred", ite); + } catch(SecurityException se) { + throw new RuntimeException("SecurityException occurred", se); + } + } + + /** + * Use {@code java.text.Normalizer#normalize(CharSequence, Normalizer.Form)} + * (but be careful, this class exists in Java 1.3, with an entirely different meaning!) + * + * @param text the text to be processed + * @return the processed string + * @throws IllegalAccessException may be thrown by a reflection call + * @throws InvocationTargetException if a reflection call throws an exception + * @throws IllegalStateException if the {@code Normalizer} class is not available + */ + private static String removeAccentsJava6(CharSequence text) + throws IllegalAccessException, InvocationTargetException { + /* + String decomposed = java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD); + return java6Pattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$ + */ + if (!java6Available || java6NormalizerFormNFD == null) { + throw new IllegalStateException("java.text.Normalizer is not available"); + } + String result; + result = (String) java6NormalizeMethod.invoke(null, new Object[] {text, java6NormalizerFormNFD}); + result = java6Pattern.matcher(result).replaceAll("");//$NON-NLS-1$ + return result; + } + + /** + * Use {@code sun.text.Normalizer#decompose(String, boolean, int)} + * + * @param text the text to be processed + * @return the processed string + * @throws IllegalAccessException may be thrown by a reflection call + * @throws InvocationTargetException if a reflection call throws an exception + * @throws IllegalStateException if the {@code Normalizer} class is not available + */ + private static String removeAccentsSUN(CharSequence text) + throws IllegalAccessException, InvocationTargetException { + /* + String decomposed = sun.text.Normalizer.decompose(text, false, 0); + return sunPattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$ + */ + if (! sunAvailable) { + throw new IllegalStateException("sun.text.Normalizer is not available"); + } + String result; + result = (String) sunDecomposeMethod.invoke(null, new Object[] {text, Boolean.FALSE, Integer.valueOf(0)}); + result = sunPattern.matcher(result).replaceAll("");//$NON-NLS-1$ + return result; + } + + // SUN internal, Java 1.3 -> Java 5 + private static boolean sunAvailable = false; + private static Method sunDecomposeMethod = null; + private static final Pattern sunPattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");//$NON-NLS-1$ + // Java 6+ + private static boolean java6Available = false; + private static Method java6NormalizeMethod = null; + private static Object java6NormalizerFormNFD = null; + private static final Pattern java6Pattern = sunPattern; + + static { + try { + // java.text.Normalizer.normalize(CharSequence, Normalizer.Form.NFD); + // Be careful not to get Java 1.3 java.text.Normalizer! + Class<?> normalizerFormClass = Thread.currentThread().getContextClassLoader() + .loadClass("java.text.Normalizer$Form");//$NON-NLS-1$ + java6NormalizerFormNFD = normalizerFormClass.getField("NFD").get(null);//$NON-NLS-1$ + Class<?> normalizerClass = Thread.currentThread().getContextClassLoader() + .loadClass("java.text.Normalizer");//$NON-NLS-1$ + java6NormalizeMethod = normalizerClass.getMethod("normalize", + new Class[] {CharSequence.class, normalizerFormClass});//$NON-NLS-1$ + java6Available = true; + } catch (ClassNotFoundException e) { + java6Available = false; + } catch (NoSuchFieldException e) { + java6Available = false; + } catch (IllegalAccessException e) { + java6Available = false; + } catch (NoSuchMethodException e) { + java6Available = false; + } + + try { + // sun.text.Normalizer.decompose(text, false, 0); + Class<?> normalizerClass = Thread.currentThread().getContextClassLoader() + .loadClass("sun.text.Normalizer");//$NON-NLS-1$ + sunDecomposeMethod = normalizerClass.getMethod("decompose", + new Class[] {String.class, Boolean.TYPE, Integer.TYPE});//$NON-NLS-1$ + sunAvailable = true; + } catch (ClassNotFoundException e) { + sunAvailable = false; + } catch (NoSuchMethodException e) { + sunAvailable = false; + } catch (java.security.AccessControlException e) { + // LANG-744 - thrown in Google App Engine + sunAvailable = false; + } + } + + // Equals + //----------------------------------------------------------------------- + /** + * <p>Compares two CharSequences, returning {@code true} if they are equal.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.</p> + * + * <pre> + * StringUtils.equals(null, null) = true + * StringUtils.equals(null, "abc") = false + * StringUtils.equals("abc", null) = false + * StringUtils.equals("abc", "abc") = true + * StringUtils.equals("abc", "ABC") = false + * </pre> + * + * @see java.lang.String#equals(Object) + * @param cs1 the first CharSequence, may be null + * @param cs2 the second CharSequence, may be null + * @return {@code true} if the CharSequences are equal, case sensitive, or + * both {@code null} + * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence) + */ + public static boolean equals(CharSequence cs1, CharSequence cs2) { + return cs1 == null ? cs2 == null : cs1.equals(cs2); + } + + /** + * <p>Compares two CharSequences, returning {@code true} if they are equal ignoring + * the case.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered equal. Comparison is case insensitive.</p> + * + * <pre> + * StringUtils.equalsIgnoreCase(null, null) = true + * StringUtils.equalsIgnoreCase(null, "abc") = false + * StringUtils.equalsIgnoreCase("abc", null) = false + * StringUtils.equalsIgnoreCase("abc", "abc") = true + * StringUtils.equalsIgnoreCase("abc", "ABC") = true + * </pre> + * + * @param str1 the first CharSequence, may be null + * @param str2 the second CharSequence, may be null + * @return {@code true} if the CharSequence are equal, case insensitive, or + * both {@code null} + * @since 3.0 Changed signature from equalsIgnoreCase(String, String) to equalsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) { + if (str1 == null || str2 == null) { + return str1 == str2; + } else { + return CharSequenceUtils.regionMatches(str1, true, 0, str2, 0, Math.max(str1.length(), str2.length())); + } + } + + // IndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(int, int)} if possible.</p> + * + * <p>A {@code null} or empty ("") CharSequence will return {@code INDEX_NOT_FOUND (-1)}.</p> + * + * <pre> + * StringUtils.indexOf(null, *) = -1 + * StringUtils.indexOf("", *) = -1 + * StringUtils.indexOf("aabaabaa", 'a') = 0 + * StringUtils.indexOf("aabaabaa", 'b') = 2 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return the first index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, int) to indexOf(CharSequence, int) + */ + public static int indexOf(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchChar, 0); + } + + /** + * <p>Finds the first index within a CharSequence from a start position, + * handling {@code null}. + * This method uses {@link String#indexOf(int, int)} if possible.</p> + * + * <p>A {@code null} or empty ("") CharSequence will return {@code (INDEX_NOT_FOUND) -1}. + * A negative start position is treated as zero. + * A start position greater than the string length returns {@code -1}.</p> + * + * <pre> + * StringUtils.indexOf(null, *, *) = -1 + * StringUtils.indexOf("", *, *) = -1 + * StringUtils.indexOf("aabaabaa", 'b', 0) = 2 + * StringUtils.indexOf("aabaabaa", 'b', 3) = 5 + * StringUtils.indexOf("aabaabaa", 'b', 9) = -1 + * StringUtils.indexOf("aabaabaa", 'b', -1) = 2 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @param startPos the start position, negative treated as zero + * @return the first index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, int, int) to indexOf(CharSequence, int, int) + */ + public static int indexOf(CharSequence seq, int searchChar, int startPos) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchChar, startPos); + } + + /** + * <p>Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String, int)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}.</p> + * + * <pre> + * StringUtils.indexOf(null, *) = -1 + * StringUtils.indexOf(*, null) = -1 + * StringUtils.indexOf("", "") = 0 + * StringUtils.indexOf("", *) = -1 (except when * = "") + * StringUtils.indexOf("aabaabaa", "a") = 0 + * StringUtils.indexOf("aabaabaa", "b") = 2 + * StringUtils.indexOf("aabaabaa", "ab") = 1 + * StringUtils.indexOf("aabaabaa", "") = 0 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, String) to indexOf(CharSequence, CharSequence) + */ + public static int indexOf(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchSeq, 0); + } + + /** + * <p>Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String, int)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.</p> + * + * <pre> + * StringUtils.indexOf(null, *, *) = -1 + * StringUtils.indexOf(*, null, *) = -1 + * StringUtils.indexOf("", "", 0) = 0 + * StringUtils.indexOf("", *, 0) = -1 (except when * = "") + * StringUtils.indexOf("aabaabaa", "a", 0) = 0 + * StringUtils.indexOf("aabaabaa", "b", 0) = 2 + * StringUtils.indexOf("aabaabaa", "ab", 0) = 1 + * StringUtils.indexOf("aabaabaa", "b", 3) = 5 + * StringUtils.indexOf("aabaabaa", "b", 9) = -1 + * StringUtils.indexOf("aabaabaa", "b", -1) = 2 + * StringUtils.indexOf("aabaabaa", "", 2) = 2 + * StringUtils.indexOf("abc", "", 9) = 3 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, String, int) to indexOf(CharSequence, CharSequence, int) + */ + public static int indexOf(CharSequence seq, CharSequence searchSeq, int startPos) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchSeq, startPos); + } + + /** + * <p>Finds the n-th index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}.</p> + * + * <pre> + * StringUtils.ordinalIndexOf(null, *, *) = -1 + * StringUtils.ordinalIndexOf(*, null, *) = -1 + * StringUtils.ordinalIndexOf("", "", *) = 0 + * StringUtils.ordinalIndexOf("aabaabaa", "a", 1) = 0 + * StringUtils.ordinalIndexOf("aabaabaa", "a", 2) = 1 + * StringUtils.ordinalIndexOf("aabaabaa", "b", 1) = 2 + * StringUtils.ordinalIndexOf("aabaabaa", "b", 2) = 5 + * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1 + * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4 + * StringUtils.ordinalIndexOf("aabaabaa", "", 1) = 0 + * StringUtils.ordinalIndexOf("aabaabaa", "", 2) = 0 + * </pre> + * + * <p>Note that 'head(CharSequence str, int n)' may be implemented as: </p> + * + * <pre> + * str.substring(0, lastOrdinalIndexOf(str, "\n", n)) + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th {@code searchStr} to find + * @return the n-th index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + * @since 2.1 + * @since 3.0 Changed signature from ordinalIndexOf(String, String, int) to ordinalIndexOf(CharSequence, CharSequence, int) + */ + public static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, false); + } + + /** + * <p>Finds the n-th index within a String, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}.</p> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th {@code searchStr} to find + * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf() + * @return the n-th index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + */ + // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int) + private static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal, boolean lastIndex) { + if (str == null || searchStr == null || ordinal <= 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return lastIndex ? str.length() : 0; + } + int found = 0; + int index = lastIndex ? str.length() : INDEX_NOT_FOUND; + do { + if (lastIndex) { + index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1); + } else { + index = CharSequenceUtils.indexOf(str, searchStr, index + 1); + } + if (index < 0) { + return index; + } + found++; + } while (found < ordinal); + return index; + } + + /** + * <p>Case in-sensitive find of the first index within a CharSequence.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.</p> + * + * <pre> + * StringUtils.indexOfIgnoreCase(null, *) = -1 + * StringUtils.indexOfIgnoreCase(*, null) = -1 + * StringUtils.indexOfIgnoreCase("", "") = 0 + * StringUtils.indexOfIgnoreCase("aabaabaa", "a") = 0 + * StringUtils.indexOfIgnoreCase("aabaabaa", "b") = 2 + * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from indexOfIgnoreCase(String, String) to indexOfIgnoreCase(CharSequence, CharSequence) + */ + public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + return indexOfIgnoreCase(str, searchStr, 0); + } + + /** + * <p>Case in-sensitive find of the first index within a CharSequence + * from the specified position.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.</p> + * + * <pre> + * StringUtils.indexOfIgnoreCase(null, *, *) = -1 + * StringUtils.indexOfIgnoreCase(*, null, *) = -1 + * StringUtils.indexOfIgnoreCase("", "", 0) = 0 + * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0) = 0 + * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0) = 2 + * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1 + * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3) = 5 + * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9) = -1 + * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2 + * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2) = 2 + * StringUtils.indexOfIgnoreCase("abc", "", 9) = 3 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from indexOfIgnoreCase(String, String, int) to indexOfIgnoreCase(CharSequence, CharSequence, int) + */ + public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos < 0) { + startPos = 0; + } + int endLimit = (str.length() - searchStr.length()) + 1; + if (startPos > endLimit) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + for (int i = startPos; i < endLimit; i++) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + // LastIndexOf + //----------------------------------------------------------------------- + /** + * <p>Finds the last index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(int)} if possible.</p> + * + * <p>A {@code null} or empty ("") CharSequence will return {@code -1}.</p> + * + * <pre> + * StringUtils.lastIndexOf(null, *) = -1 + * StringUtils.lastIndexOf("", *) = -1 + * StringUtils.lastIndexOf("aabaabaa", 'a') = 7 + * StringUtils.lastIndexOf("aabaabaa", 'b') = 5 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return the last index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, int) to lastIndexOf(CharSequence, int) + */ + public static int lastIndexOf(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length()); + } + + /** + * <p>Finds the last index within a CharSequence from a start position, + * handling {@code null}. + * This method uses {@link String#lastIndexOf(int, int)} if possible.</p> + * + * <p>A {@code null} or empty ("") CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * A start position greater than the string length searches the whole string.</p> + * + * <pre> + * StringUtils.lastIndexOf(null, *, *) = -1 + * StringUtils.lastIndexOf("", *, *) = -1 + * StringUtils.lastIndexOf("aabaabaa", 'b', 8) = 5 + * StringUtils.lastIndexOf("aabaabaa", 'b', 4) = 2 + * StringUtils.lastIndexOf("aabaabaa", 'b', 0) = -1 + * StringUtils.lastIndexOf("aabaabaa", 'b', 9) = 5 + * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1 + * StringUtils.lastIndexOf("aabaabaa", 'a', 0) = 0 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @param startPos the start position + * @return the last index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, int, int) to lastIndexOf(CharSequence, int, int) + */ + public static int lastIndexOf(CharSequence seq, int searchChar, int startPos) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos); + } + + /** + * <p>Finds the last index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(String)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}.</p> + * + * <pre> + * StringUtils.lastIndexOf(null, *) = -1 + * StringUtils.lastIndexOf(*, null) = -1 + * StringUtils.lastIndexOf("", "") = 0 + * StringUtils.lastIndexOf("aabaabaa", "a") = 7 + * StringUtils.lastIndexOf("aabaabaa", "b") = 5 + * StringUtils.lastIndexOf("aabaabaa", "ab") = 4 + * StringUtils.lastIndexOf("aabaabaa", "") = 8 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return the last index of the search String, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, String) to lastIndexOf(CharSequence, CharSequence) + */ + public static int lastIndexOf(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchSeq, seq.length()); + } + + /** + * <p>Finds the n-th last index within a String, handling {@code null}. + * This method uses {@link String#lastIndexOf(String)}.</p> + * + * <p>A {@code null} String will return {@code -1}.</p> + * + * <pre> + * StringUtils.lastOrdinalIndexOf(null, *, *) = -1 + * StringUtils.lastOrdinalIndexOf(*, null, *) = -1 + * StringUtils.lastOrdinalIndexOf("", "", *) = 0 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1) = 7 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2) = 6 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1) = 5 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2) = 2 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1) = 8 + * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2) = 8 + * </pre> + * + * <p>Note that 'tail(CharSequence str, int n)' may be implemented as: </p> + * + * <pre> + * str.substring(lastOrdinalIndexOf(str, "\n", n) + 1) + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th last {@code searchStr} to find + * @return the n-th last index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from lastOrdinalIndexOf(String, String, int) to lastOrdinalIndexOf(CharSequence, CharSequence, int) + */ + public static int lastOrdinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, true); + } + + /** + * <p>Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(String, int)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.</p> + * + * <pre> + * StringUtils.lastIndexOf(null, *, *) = -1 + * StringUtils.lastIndexOf(*, null, *) = -1 + * StringUtils.lastIndexOf("aabaabaa", "a", 8) = 7 + * StringUtils.lastIndexOf("aabaabaa", "b", 8) = 5 + * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4 + * StringUtils.lastIndexOf("aabaabaa", "b", 9) = 5 + * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1 + * StringUtils.lastIndexOf("aabaabaa", "a", 0) = 0 + * StringUtils.lastIndexOf("aabaabaa", "b", 0) = -1 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, String, int) to lastIndexOf(CharSequence, CharSequence, int) + */ + public static int lastIndexOf(CharSequence seq, CharSequence searchSeq, int startPos) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchSeq, startPos); + } + + /** + * <p>Case in-sensitive find of the last index within a CharSequence.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.</p> + * + * <pre> + * StringUtils.lastIndexOfIgnoreCase(null, *) = -1 + * StringUtils.lastIndexOfIgnoreCase(*, null) = -1 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A") = 7 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B") = 5 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String) to lastIndexOfIgnoreCase(CharSequence, CharSequence) + */ + public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + return lastIndexOfIgnoreCase(str, searchStr, str.length()); + } + + /** + * <p>Case in-sensitive find of the last index within a CharSequence + * from the specified position.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.</p> + * + * <pre> + * StringUtils.lastIndexOfIgnoreCase(null, *, *) = -1 + * StringUtils.lastIndexOfIgnoreCase(*, null, *) = -1 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8) = 7 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8) = 5 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9) = 5 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0) = 0 + * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0) = -1 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param startPos the start position + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} input + * @since 2.5 + * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String, int) to lastIndexOfIgnoreCase(CharSequence, CharSequence, int) + */ + public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos > (str.length() - searchStr.length())) { + startPos = str.length() - searchStr.length(); + } + if (startPos < 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + + for (int i = startPos; i >= 0; i--) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + // Contains + //----------------------------------------------------------------------- + /** + * <p>Checks if CharSequence contains a search character, handling {@code null}. + * This method uses {@link String#indexOf(int)} if possible.</p> + * + * <p>A {@code null} or empty ("") CharSequence will return {@code false}.</p> + * + * <pre> + * StringUtils.contains(null, *) = false + * StringUtils.contains("", *) = false + * StringUtils.contains("abc", 'a') = true + * StringUtils.contains("abc", 'z') = false + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return true if the CharSequence contains the search character, + * false if not or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from contains(String, int) to contains(CharSequence, int) + */ + public static boolean contains(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return false; + } + return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0; + } + + /** + * <p>Checks if CharSequence contains a search CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.</p> + * + * <p>A {@code null} CharSequence will return {@code false}.</p> + * + * <pre> + * StringUtils.contains(null, *) = false + * StringUtils.contains(*, null) = false + * StringUtils.contains("", "") = true + * StringUtils.contains("abc", "") = true + * StringUtils.contains("abc", "a") = true + * StringUtils.contains("abc", "z") = false + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return true if the CharSequence contains the search CharSequence, + * false if not or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from contains(String, String) to contains(CharSequence, CharSequence) + */ + public static boolean contains(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return false; + } + return CharSequenceUtils.indexOf(seq, searchSeq, 0) >= 0; + } + + /** + * <p>Checks if CharSequence contains a search CharSequence irrespective of case, + * handling {@code null}. Case-insensitivity is defined as by + * {@link String#equalsIgnoreCase(String)}. + * + * <p>A {@code null} CharSequence will return {@code false}.</p> + * + * <pre> + * StringUtils.contains(null, *) = false + * StringUtils.contains(*, null) = false + * StringUtils.contains("", "") = true + * StringUtils.contains("abc", "") = true + * StringUtils.contains("abc", "a") = true + * StringUtils.contains("abc", "z") = false + * StringUtils.contains("abc", "A") = true + * StringUtils.contains("abc", "Z") = false + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return true if the CharSequence contains the search CharSequence irrespective of + * case or false if not or {@code null} string input + * @since 3.0 Changed signature from containsIgnoreCase(String, String) to containsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) { + if (str == null || searchStr == null) { + return false; + } + int len = searchStr.length(); + int max = str.length() - len; + for (int i = 0; i <= max; i++) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) { + return true; + } + } + return false; + } + + /** + * Check whether the given CharSequence contains any whitespace characters. + * @param seq the CharSequence to check (may be {@code null}) + * @return {@code true} if the CharSequence is not empty and + * contains at least 1 whitespace character + * @see java.lang.Character#isWhitespace + * @since 3.0 + */ + // From org.springframework.util.StringUtils, under Apache License 2.0 + public static boolean containsWhitespace(CharSequence seq) { + if (isEmpty(seq)) { + return false; + } + int strLen = seq.length(); + for (int i = 0; i < strLen; i++) { + if (Character.isWhitespace(seq.charAt(i))) { + return true; + } + } + return false; + } + + // IndexOfAny chars + //----------------------------------------------------------------------- + /** + * <p>Search a CharSequence to find the first index of any + * character in the given set of characters.</p> + * + * <p>A {@code null} String will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}.</p> + * + * <pre> + * StringUtils.indexOfAny(null, *) = -1 + * StringUtils.indexOfAny("", *) = -1 + * StringUtils.indexOfAny(*, null) = -1 + * StringUtils.indexOfAny(*, []) = -1 + * StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0 + * StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3 + * StringUtils.indexOfAny("aba", ['z']) = -1 + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAny(String, char[]) to indexOfAny(CharSequence, char...) + */ + public static int indexOfAny(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { + // ch is a supplementary character + if (searchChars[j + 1] == cs.charAt(i + 1)) { + return i; + } + } else { + return i; + } + } + } + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Search a CharSequence to find the first index of any + * character in the given set of characters.</p> + * + * <p>A {@code null} String will return {@code -1}. + * A {@code null} search string will return {@code -1}.</p> + * + * <pre> + * StringUtils.indexOfAny(null, *) = -1 + * StringUtils.indexOfAny("", *) = -1 + * StringUtils.indexOfAny(*, null) = -1 + * StringUtils.indexOfAny(*, "") = -1 + * StringUtils.indexOfAny("zzabyycdxx", "za") = 0 + * StringUtils.indexOfAny("zzabyycdxx", "by") = 3 + * StringUtils.indexOfAny("aba","z") = -1 + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAny(String, String) to indexOfAny(CharSequence, String) + */ + public static int indexOfAny(CharSequence cs, String searchChars) { + if (isEmpty(cs) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + return indexOfAny(cs, searchChars.toCharArray()); + } + + // ContainsAny + //----------------------------------------------------------------------- + /** + * <p>Checks if the CharSequence contains any character in the given + * set of characters.</p> + * + * <p>A {@code null} CharSequence will return {@code false}. + * A {@code null} or zero length search array will return {@code false}.</p> + * + * <pre> + * StringUtils.containsAny(null, *) = false + * StringUtils.containsAny("", *) = false + * StringUtils.containsAny(*, null) = false + * StringUtils.containsAny(*, []) = false + * StringUtils.containsAny("zzabyycdxx",['z','a']) = true + * StringUtils.containsAny("zzabyycdxx",['b','y']) = true + * StringUtils.containsAny("aba", ['z']) = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the {@code true} if any of the chars are found, + * {@code false} if no match or null input + * @since 2.4 + * @since 3.0 Changed signature from containsAny(String, char[]) to containsAny(CharSequence, char...) + */ + public static boolean containsAny(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return false; + } + int csLength = cs.length(); + int searchLength = searchChars.length; + int csLast = csLength - 1; + int searchLast = searchLength - 1; + for (int i = 0; i < csLength; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLength; j++) { + if (searchChars[j] == ch) { + if (Character.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return true; + } + if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { + return true; + } + } else { + // ch is in the Basic Multilingual Plane + return true; + } + } + } + } + return false; + } + + /** + * <p> + * Checks if the CharSequence contains any character in the given set of characters. + * </p> + * + * <p> + * A {@code null} CharSequence will return {@code false}. A {@code null} search CharSequence will return + * {@code false}. + * </p> + * + * <pre> + * StringUtils.containsAny(null, *) = false + * StringUtils.containsAny("", *) = false + * StringUtils.containsAny(*, null) = false + * StringUtils.containsAny(*, "") = false + * StringUtils.containsAny("zzabyycdxx", "za") = true + * StringUtils.containsAny("zzabyycdxx", "by") = true + * StringUtils.containsAny("aba","z") = false + * </pre> + * + * @param cs + * the CharSequence to check, may be null + * @param searchChars + * the chars to search for, may be null + * @return the {@code true} if any of the chars are found, {@code false} if no match or null input + * @since 2.4 + * @since 3.0 Changed signature from containsAny(String, String) to containsAny(CharSequence, CharSequence) + */ + public static boolean containsAny(CharSequence cs, CharSequence searchChars) { + if (searchChars == null) { + return false; + } + return containsAny(cs, CharSequenceUtils.toCharArray(searchChars)); + } + + // IndexOfAnyBut chars + //----------------------------------------------------------------------- + /** + * <p>Searches a CharSequence to find the first index of any + * character not in the given set of characters.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}.</p> + * + * <pre> + * StringUtils.indexOfAnyBut(null, *) = -1 + * StringUtils.indexOfAnyBut("", *) = -1 + * StringUtils.indexOfAnyBut(*, null) = -1 + * StringUtils.indexOfAnyBut(*, []) = -1 + * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3 + * StringUtils.indexOfAnyBut("aba", new char[] {'z'} ) = 0 + * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} ) = -1 + + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to indexOfAnyBut(CharSequence, char...) + */ + public static int indexOfAnyBut(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + outer: + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { + if (searchChars[j + 1] == cs.charAt(i + 1)) { + continue outer; + } + } else { + continue outer; + } + } + } + return i; + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Search a CharSequence to find the first index of any + * character not in the given set of characters.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A {@code null} or empty search string will return {@code -1}.</p> + * + * <pre> + * StringUtils.indexOfAnyBut(null, *) = -1 + * StringUtils.indexOfAnyBut("", *) = -1 + * StringUtils.indexOfAnyBut(*, null) = -1 + * StringUtils.indexOfAnyBut(*, "") = -1 + * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3 + * StringUtils.indexOfAnyBut("zzabyycdxx", "") = -1 + * StringUtils.indexOfAnyBut("aba","ab") = -1 + * </pre> + * + * @param seq the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAnyBut(String, String) to indexOfAnyBut(CharSequence, CharSequence) + */ + public static int indexOfAnyBut(CharSequence seq, CharSequence searchChars) { + if (isEmpty(seq) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int strLen = seq.length(); + for (int i = 0; i < strLen; i++) { + char ch = seq.charAt(i); + boolean chFound = CharSequenceUtils.indexOf(searchChars, ch, 0) >= 0; + if (i + 1 < strLen && Character.isHighSurrogate(ch)) { + char ch2 = seq.charAt(i + 1); + if (chFound && CharSequenceUtils.indexOf(searchChars, ch2, 0) < 0) { + return i; + } + } else { + if (!chFound) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + // ContainsOnly + //----------------------------------------------------------------------- + /** + * <p>Checks if the CharSequence contains only certain characters.</p> + * + * <p>A {@code null} CharSequence will return {@code false}. + * A {@code null} valid character array will return {@code false}. + * An empty CharSequence (length()=0) always returns {@code true}.</p> + * + * <pre> + * StringUtils.containsOnly(null, *) = false + * StringUtils.containsOnly(*, null) = false + * StringUtils.containsOnly("", *) = true + * StringUtils.containsOnly("ab", '') = false + * StringUtils.containsOnly("abab", 'abc') = true + * StringUtils.containsOnly("ab1", 'abc') = false + * StringUtils.containsOnly("abz", 'abc') = false + * </pre> + * + * @param cs the String to check, may be null + * @param valid an array of valid chars, may be null + * @return true if it only contains valid chars and is non-null + * @since 3.0 Changed signature from containsOnly(String, char[]) to containsOnly(CharSequence, char...) + */ + public static boolean containsOnly(CharSequence cs, char... valid) { + // All these pre-checks are to maintain API with an older version + if (valid == null || cs == null) { + return false; + } + if (cs.length() == 0) { + return true; + } + if (valid.length == 0) { + return false; + } + return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND; + } + + /** + * <p>Checks if the CharSequence contains only certain characters.</p> + * + * <p>A {@code null} CharSequence will return {@code false}. + * A {@code null} valid character String will return {@code false}. + * An empty String (length()=0) always returns {@code true}.</p> + * + * <pre> + * StringUtils.containsOnly(null, *) = false + * StringUtils.containsOnly(*, null) = false + * StringUtils.containsOnly("", *) = true + * StringUtils.containsOnly("ab", "") = false + * StringUtils.containsOnly("abab", "abc") = true + * StringUtils.containsOnly("ab1", "abc") = false + * StringUtils.containsOnly("abz", "abc") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param validChars a String of valid chars, may be null + * @return true if it only contains valid chars and is non-null + * @since 2.0 + * @since 3.0 Changed signature from containsOnly(String, String) to containsOnly(CharSequence, String) + */ + public static boolean containsOnly(CharSequence cs, String validChars) { + if (cs == null || validChars == null) { + return false; + } + return containsOnly(cs, validChars.toCharArray()); + } + + // ContainsNone + //----------------------------------------------------------------------- + /** + * <p>Checks that the CharSequence does not contain certain characters.</p> + * + * <p>A {@code null} CharSequence will return {@code true}. + * A {@code null} invalid character array will return {@code true}. + * An empty CharSequence (length()=0) always returns true.</p> + * + * <pre> + * StringUtils.containsNone(null, *) = true + * StringUtils.containsNone(*, null) = true + * StringUtils.containsNone("", *) = true + * StringUtils.containsNone("ab", '') = true + * StringUtils.containsNone("abab", 'xyz') = true + * StringUtils.containsNone("ab1", 'xyz') = true + * StringUtils.containsNone("abz", 'xyz') = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param searchChars an array of invalid chars, may be null + * @return true if it contains none of the invalid chars, or is null + * @since 2.0 + * @since 3.0 Changed signature from containsNone(String, char[]) to containsNone(CharSequence, char...) + */ + public static boolean containsNone(CharSequence cs, char... searchChars) { + if (cs == null || searchChars == null) { + return true; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (Character.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return false; + } + if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { + return false; + } + } else { + // ch is in the Basic Multilingual Plane + return false; + } + } + } + } + return true; + } + + /** + * <p>Checks that the CharSequence does not contain certain characters.</p> + * + * <p>A {@code null} CharSequence will return {@code true}. + * A {@code null} invalid character array will return {@code true}. + * An empty String ("") always returns true.</p> + * + * <pre> + * StringUtils.containsNone(null, *) = true + * StringUtils.containsNone(*, null) = true + * StringUtils.containsNone("", *) = true + * StringUtils.containsNone("ab", "") = true + * StringUtils.containsNone("abab", "xyz") = true + * StringUtils.containsNone("ab1", "xyz") = true + * StringUtils.containsNone("abz", "xyz") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @param invalidChars a String of invalid chars, may be null + * @return true if it contains none of the invalid chars, or is null + * @since 2.0 + * @since 3.0 Changed signature from containsNone(String, String) to containsNone(CharSequence, String) + */ + public static boolean containsNone(CharSequence cs, String invalidChars) { + if (cs == null || invalidChars == null) { + return true; + } + return containsNone(cs, invalidChars.toCharArray()); + } + + // IndexOfAny strings + //----------------------------------------------------------------------- + /** + * <p>Find the first index of any of a set of potential substrings.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}. + * A {@code null} search array entry will be ignored, but a search + * array containing "" will return {@code 0} if {@code str} is not + * null. This method uses {@link String#indexOf(String)} if possible.</p> + * + * <pre> + * StringUtils.indexOfAny(null, *) = -1 + * StringUtils.indexOfAny(*, null) = -1 + * StringUtils.indexOfAny(*, []) = -1 + * StringUtils.indexOfAny("zzabyycdxx", ["ab","cd"]) = 2 + * StringUtils.indexOfAny("zzabyycdxx", ["cd","ab"]) = 2 + * StringUtils.indexOfAny("zzabyycdxx", ["mn","op"]) = -1 + * StringUtils.indexOfAny("zzabyycdxx", ["zab","aby"]) = 1 + * StringUtils.indexOfAny("zzabyycdxx", [""]) = 0 + * StringUtils.indexOfAny("", [""]) = 0 + * StringUtils.indexOfAny("", ["a"]) = -1 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStrs the CharSequences to search for, may be null + * @return the first index of any of the searchStrs in str, -1 if no match + * @since 3.0 Changed signature from indexOfAny(String, String[]) to indexOfAny(CharSequence, CharSequence...) + */ + public static int indexOfAny(CharSequence str, CharSequence... searchStrs) { + if (str == null || searchStrs == null) { + return INDEX_NOT_FOUND; + } + int sz = searchStrs.length; + + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp = 0; + for (int i = 0; i < sz; i++) { + CharSequence search = searchStrs[i]; + if (search == null) { + continue; + } + tmp = CharSequenceUtils.indexOf(str, search, 0); + if (tmp == INDEX_NOT_FOUND) { + continue; + } + + if (tmp < ret) { + ret = tmp; + } + } + + return (ret == Integer.MAX_VALUE) ? INDEX_NOT_FOUND : ret; + } + + /** + * <p>Find the latest index of any of a set of potential substrings.</p> + * + * <p>A {@code null} CharSequence will return {@code -1}. + * A {@code null} search array will return {@code -1}. + * A {@code null} or zero length search array entry will be ignored, + * but a search array containing "" will return the length of {@code str} + * if {@code str} is not null. This method uses {@link String#indexOf(String)} if possible</p> + * + * <pre> + * StringUtils.lastIndexOfAny(null, *) = -1 + * StringUtils.lastIndexOfAny(*, null) = -1 + * StringUtils.lastIndexOfAny(*, []) = -1 + * StringUtils.lastIndexOfAny(*, [null]) = -1 + * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6 + * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6 + * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1 + * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1 + * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""]) = 10 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param searchStrs the CharSequences to search for, may be null + * @return the last index of any of the CharSequences, -1 if no match + * @since 3.0 Changed signature from lastIndexOfAny(String, String[]) to lastIndexOfAny(CharSequence, CharSequence) + */ + public static int lastIndexOfAny(CharSequence str, CharSequence... searchStrs) { + if (str == null || searchStrs == null) { + return INDEX_NOT_FOUND; + } + int sz = searchStrs.length; + int ret = INDEX_NOT_FOUND; + int tmp = 0; + for (int i = 0; i < sz; i++) { + CharSequence search = searchStrs[i]; + if (search == null) { + continue; + } + tmp = CharSequenceUtils.lastIndexOf(str, search, str.length()); + if (tmp > ret) { + ret = tmp; + } + } + return ret; + } + + // Substring + //----------------------------------------------------------------------- + /** + * <p>Gets a substring from the specified String avoiding exceptions.</p> + * + * <p>A negative start position can be used to start {@code n} + * characters from the end of the String.</p> + * + * <p>A {@code null} String will return {@code null}. + * An empty ("") String will return "".</p> + * + * <pre> + * StringUtils.substring(null, *) = null + * StringUtils.substring("", *) = "" + * StringUtils.substring("abc", 0) = "abc" + * StringUtils.substring("abc", 2) = "c" + * StringUtils.substring("abc", 4) = "" + * StringUtils.substring("abc", -2) = "bc" + * StringUtils.substring("abc", -4) = "abc" + * </pre> + * + * @param str the String to get the substring from, may be null + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position, {@code null} if null String input + */ + public static String substring(String str, int start) { + if (str == null) { + return null; + } + + // handle negatives, which means last n characters + if (start < 0) { + start = str.length() + start; // remember start is negative + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return EMPTY; + } + + return str.substring(start); + } + + /** + * <p>Gets a substring from the specified String avoiding exceptions.</p> + * + * <p>A negative start position can be used to start/end {@code n} + * characters from the end of the String.</p> + * + * <p>The returned substring starts with the character in the {@code start} + * position and ends before the {@code end} position. All position counting is + * zero-based -- i.e., to start at the beginning of the string use + * {@code start = 0}. Negative start and end positions can be used to + * specify offsets relative to the end of the String.</p> + * + * <p>If {@code start} is not strictly to the left of {@code end}, "" + * is returned.</p> + * + * <pre> + * StringUtils.substring(null, *, *) = null + * StringUtils.substring("", * , *) = ""; + * StringUtils.substring("abc", 0, 2) = "ab" + * StringUtils.substring("abc", 2, 0) = "" + * StringUtils.substring("abc", 2, 4) = "c" + * StringUtils.substring("abc", 4, 6) = "" + * StringUtils.substring("abc", 2, 2) = "" + * StringUtils.substring("abc", -2, -1) = "b" + * StringUtils.substring("abc", -4, 2) = "ab" + * </pre> + * + * @param str the String to get the substring from, may be null + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position, + * {@code null} if null String input + */ + public static String substring(String str, int start, int end) { + if (str == null) { + return null; + } + + // handle negatives + if (end < 0) { + end = str.length() + end; // remember end is negative + } + if (start < 0) { + start = str.length() + start; // remember start is negative + } + + // check length next + if (end > str.length()) { + end = str.length(); + } + + // if start is greater than end, return "" + if (start > end) { + return EMPTY; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + // Left/Right/Mid + //----------------------------------------------------------------------- + /** + * <p>Gets the leftmost {@code len} characters of a String.</p> + * + * <p>If {@code len} characters are not available, or the + * String is {@code null}, the String will be returned without + * an exception. An empty String is returned if len is negative.</p> + * + * <pre> + * StringUtils.left(null, *) = null + * StringUtils.left(*, -ve) = "" + * StringUtils.left("", *) = "" + * StringUtils.left("abc", 0) = "" + * StringUtils.left("abc", 2) = "ab" + * StringUtils.left("abc", 4) = "abc" + * </pre> + * + * @param str the String to get the leftmost characters from, may be null + * @param len the length of the required String + * @return the leftmost characters, {@code null} if null String input + */ + public static String left(String str, int len) { + if (str == null) { + return null; + } + if (len < 0) { + return EMPTY; + } + if (str.length() <= len) { + return str; + } + return str.substring(0, len); + } + + /** + * <p>Gets the rightmost {@code len} characters of a String.</p> + * + * <p>If {@code len} characters are not available, or the String + * is {@code null}, the String will be returned without an + * an exception. An empty String is returned if len is negative.</p> + * + * <pre> + * StringUtils.right(null, *) = null + * StringUtils.right(*, -ve) = "" + * StringUtils.right("", *) = "" + * StringUtils.right("abc", 0) = "" + * StringUtils.right("abc", 2) = "bc" + * StringUtils.right("abc", 4) = "abc" + * </pre> + * + * @param str the String to get the rightmost characters from, may be null + * @param len the length of the required String + * @return the rightmost characters, {@code null} if null String input + */ + public static String right(String str, int len) { + if (str == null) { + return null; + } + if (len < 0) { + return EMPTY; + } + if (str.length() <= len) { + return str; + } + return str.substring(str.length() - len); + } + + /** + * <p>Gets {@code len} characters from the middle of a String.</p> + * + * <p>If {@code len} characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is {@code null}, {@code null} will be returned. + * An empty String is returned if len is negative or exceeds the + * length of {@code str}.</p> + * + * <pre> + * StringUtils.mid(null, *, *) = null + * StringUtils.mid(*, *, -ve) = "" + * StringUtils.mid("", 0, *) = "" + * StringUtils.mid("abc", 0, 2) = "ab" + * StringUtils.mid("abc", 0, 4) = "abc" + * StringUtils.mid("abc", 2, 4) = "c" + * StringUtils.mid("abc", 4, 2) = "" + * StringUtils.mid("abc", -2, 2) = "ab" + * </pre> + * + * @param str the String to get the characters from, may be null + * @param pos the position to start from, negative treated as zero + * @param len the length of the required String + * @return the middle characters, {@code null} if null String input + */ + public static String mid(String str, int pos, int len) { + if (str == null) { + return null; + } + if (len < 0 || pos > str.length()) { + return EMPTY; + } + if (pos < 0) { + pos = 0; + } + if (str.length() <= (pos + len)) { + return str.substring(pos); + } + return str.substring(pos, pos + len); + } + + // SubStringAfter/SubStringBefore + //----------------------------------------------------------------------- + /** + * <p>Gets the substring before the first occurrence of a separator. + * The separator is not returned.</p> + * + * <p>A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * A {@code null} separator will return the input string.</p> + * + * <p>If nothing is found, the string input is returned.</p> + * + * <pre> + * StringUtils.substringBefore(null, *) = null + * StringUtils.substringBefore("", *) = "" + * StringUtils.substringBefore("abc", "a") = "" + * StringUtils.substringBefore("abcba", "b") = "a" + * StringUtils.substringBefore("abc", "c") = "ab" + * StringUtils.substringBefore("abc", "d") = "abc" + * StringUtils.substringBefore("abc", "") = "" + * StringUtils.substringBefore("abc", null) = "abc" + * </pre> + * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring before the first occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringBefore(String str, String separator) { + if (isEmpty(str) || separator == null) { + return str; + } + if (separator.length() == 0) { + return EMPTY; + } + int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + /** + * <p>Gets the substring after the first occurrence of a separator. + * The separator is not returned.</p> + * + * <p>A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * A {@code null} separator will return the empty string if the + * input string is not {@code null}.</p> + * + * <p>If nothing is found, the empty string is returned.</p> + * + * <pre> + * StringUtils.substringAfter(null, *) = null + * StringUtils.substringAfter("", *) = "" + * StringUtils.substringAfter(*, null) = "" + * StringUtils.substringAfter("abc", "a") = "bc" + * StringUtils.substringAfter("abcba", "b") = "cba" + * StringUtils.substringAfter("abc", "c") = "" + * StringUtils.substringAfter("abc", "d") = "" + * StringUtils.substringAfter("abc", "") = "abc" + * </pre> + * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the first occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringAfter(String str, String separator) { + if (isEmpty(str)) { + return str; + } + if (separator == null) { + return EMPTY; + } + int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + /** + * <p>Gets the substring before the last occurrence of a separator. + * The separator is not returned.</p> + * + * <p>A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * An empty or {@code null} separator will return the input string.</p> + * + * <p>If nothing is found, the string input is returned.</p> + * + * <pre> + * StringUtils.substringBeforeLast(null, *) = null + * StringUtils.substringBeforeLast("", *) = "" + * StringUtils.substringBeforeLast("abcba", "b") = "abc" + * StringUtils.substringBeforeLast("abc", "c") = "ab" + * StringUtils.substringBeforeLast("a", "a") = "" + * StringUtils.substringBeforeLast("a", "z") = "a" + * StringUtils.substringBeforeLast("a", null) = "a" + * StringUtils.substringBeforeLast("a", "") = "a" + * </pre> + * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring before the last occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringBeforeLast(String str, String separator) { + if (isEmpty(str) || isEmpty(separator)) { + return str; + } + int pos = str.lastIndexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + /** + * <p>Gets the substring after the last occurrence of a separator. + * The separator is not returned.</p> + * + * <p>A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * An empty or {@code null} separator will return the empty string if + * the input string is not {@code null}.</p> + * + * <p>If nothing is found, the empty string is returned.</p> + * + * <pre> + * StringUtils.substringAfterLast(null, *) = null + * StringUtils.substringAfterLast("", *) = "" + * StringUtils.substringAfterLast(*, "") = "" + * StringUtils.substringAfterLast(*, null) = "" + * StringUtils.substringAfterLast("abc", "a") = "bc" + * StringUtils.substringAfterLast("abcba", "b") = "a" + * StringUtils.substringAfterLast("abc", "c") = "" + * StringUtils.substringAfterLast("a", "a") = "" + * StringUtils.substringAfterLast("a", "z") = "" + * </pre> + * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the last occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringAfterLast(String str, String separator) { + if (isEmpty(str)) { + return str; + } + if (isEmpty(separator)) { + return EMPTY; + } + int pos = str.lastIndexOf(separator); + if (pos == INDEX_NOT_FOUND || pos == (str.length() - separator.length())) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + // Substring between + //----------------------------------------------------------------------- + /** + * <p>Gets the String that is nested in between two instances of the + * same String.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} tag returns {@code null}.</p> + * + * <pre> + * StringUtils.substringBetween(null, *) = null + * StringUtils.substringBetween("", "") = "" + * StringUtils.substringBetween("", "tag") = null + * StringUtils.substringBetween("tagabctag", null) = null + * StringUtils.substringBetween("tagabctag", "") = "" + * StringUtils.substringBetween("tagabctag", "tag") = "abc" + * </pre> + * + * @param str the String containing the substring, may be null + * @param tag the String before and after the substring, may be null + * @return the substring, {@code null} if no match + * @since 2.0 + */ + public static String substringBetween(String str, String tag) { + return substringBetween(str, tag, tag); + } + + /** + * <p>Gets the String that is nested in between two Strings. + * Only the first match is returned.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} open/close returns {@code null} (no match). + * An empty ("") open and close returns an empty string.</p> + * + * <pre> + * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b" + * StringUtils.substringBetween(null, *, *) = null + * StringUtils.substringBetween(*, null, *) = null + * StringUtils.substringBetween(*, *, null) = null + * StringUtils.substringBetween("", "", "") = "" + * StringUtils.substringBetween("", "", "]") = null + * StringUtils.substringBetween("", "[", "]") = null + * StringUtils.substringBetween("yabcz", "", "") = "" + * StringUtils.substringBetween("yabcz", "y", "z") = "abc" + * StringUtils.substringBetween("yabczyabcz", "y", "z") = "abc" + * </pre> + * + * @param str the String containing the substring, may be null + * @param open the String before the substring, may be null + * @param close the String after the substring, may be null + * @return the substring, {@code null} if no match + * @since 2.0 + */ + public static String substringBetween(String str, String open, String close) { + if (str == null || open == null || close == null) { + return null; + } + int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } + + /** + * <p>Searches a String for substrings delimited by a start and end tag, + * returning all matching substrings in an array.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} open/close returns {@code null} (no match). + * An empty ("") open/close returns {@code null} (no match).</p> + * + * <pre> + * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"] + * StringUtils.substringsBetween(null, *, *) = null + * StringUtils.substringsBetween(*, null, *) = null + * StringUtils.substringsBetween(*, *, null) = null + * StringUtils.substringsBetween("", "[", "]") = [] + * </pre> + * + * @param str the String containing the substrings, null returns null, empty returns empty + * @param open the String identifying the start of the substring, empty returns null + * @param close the String identifying the end of the substring, empty returns null + * @return a String Array of substrings, or {@code null} if no match + * @since 2.3 + */ + public static String[] substringsBetween(String str, String open, String close) { + if (str == null || isEmpty(open) || isEmpty(close)) { + return null; + } + int strLen = str.length(); + if (strLen == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + int closeLen = close.length(); + int openLen = open.length(); + List<String> list = new ArrayList<String>(); + int pos = 0; + while (pos < (strLen - closeLen)) { + int start = str.indexOf(open, pos); + if (start < 0) { + break; + } + start += openLen; + int end = str.indexOf(close, start); + if (end < 0) { + break; + } + list.add(str.substring(start, end)); + pos = end + closeLen; + } + if (list.isEmpty()) { + return null; + } + return list.toArray(new String [list.size()]); + } + + // Nested extraction + //----------------------------------------------------------------------- + + // Splitting + //----------------------------------------------------------------------- + /** + * <p>Splits the provided text into an array, using whitespace as the + * separator. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.split(null) = null + * StringUtils.split("") = [] + * StringUtils.split("abc def") = ["abc", "def"] + * StringUtils.split("abc def") = ["abc", "def"] + * StringUtils.split(" abc ") = ["abc"] + * </pre> + * + * @param str the String to parse, may be null + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str) { + return split(str, null, -1); + } + + /** + * <p>Splits the provided text into an array, separator specified. + * This is an alternative to using StringTokenizer.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.split(null, *) = null + * StringUtils.split("", *) = [] + * StringUtils.split("a.b.c", '.') = ["a", "b", "c"] + * StringUtils.split("a..b.c", '.') = ["a", "b", "c"] + * StringUtils.split("a:b:c", '.') = ["a:b:c"] + * StringUtils.split("a b c", ' ') = ["a", "b", "c"] + * </pre> + * + * @param str the String to parse, may be null + * @param separatorChar the character used as the delimiter + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.0 + */ + public static String[] split(String str, char separatorChar) { + return splitWorker(str, separatorChar, false); + } + + /** + * <p>Splits the provided text into an array, separators specified. + * This is an alternative to using StringTokenizer.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.</p> + * + * <pre> + * StringUtils.split(null, *) = null + * StringUtils.split("", *) = [] + * StringUtils.split("abc def", null) = ["abc", "def"] + * StringUtils.split("abc def", " ") = ["abc", "def"] + * StringUtils.split("abc def", " ") = ["abc", "def"] + * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, false); + } + + /** + * <p>Splits the provided text into an array with a maximum length, + * separators specified.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as one separator.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.</p> + * + * <p>If more than {@code max} delimited substrings are found, the last + * returned string includes all characters after the first {@code max - 1} + * returned strings (including separator characters).</p> + * + * <pre> + * StringUtils.split(null, *, *) = null + * StringUtils.split("", *, *) = [] + * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"] + * StringUtils.split("ab de fg", null, 0) = ["ab", "cd", "ef"] + * StringUtils.split("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"] + * StringUtils.split("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, false); + } + + /** + * <p>Splits the provided text into an array, separator string specified.</p> + * + * <p>The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.</p> + * + * <pre> + * StringUtils.splitByWholeSeparator(null, *) = null + * StringUtils.splitByWholeSeparator("", *) = [] + * StringUtils.splitByWholeSeparator("ab de fg", null) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparator("ab de fg", null) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparator("ab:cd:ef", ":") = ["ab", "cd", "ef"] + * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String was input + */ + public static String[] splitByWholeSeparator(String str, String separator) { + return splitByWholeSeparatorWorker( str, separator, -1, false ) ; + } + + /** + * <p>Splits the provided text into an array, separator string specified. + * Returns a maximum of {@code max} substrings.</p> + * + * <p>The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.</p> + * + * <pre> + * StringUtils.splitByWholeSeparator(null, *, *) = null + * StringUtils.splitByWholeSeparator("", *, *) = [] + * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparator("ab de fg", null, 0) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] + * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"] + * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, {@code null} if null String was input + */ + public static String[] splitByWholeSeparator( String str, String separator, int max ) { + return splitByWholeSeparatorWorker(str, separator, max, false); + } + + /** + * <p>Splits the provided text into an array, separator string specified. </p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.</p> + * + * <pre> + * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *) = null + * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *) = [] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null) = ["ab", "", "", "de", "fg"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator) { + return splitByWholeSeparatorWorker(str, separator, -1, true); + } + + /** + * <p>Splits the provided text into an array, separator string specified. + * Returns a maximum of {@code max} substrings.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.</p> + * + * <pre> + * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *) = null + * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *) = [] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0) = ["ab", "de", "fg"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0) = ["ab", "", "", "de", "fg"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"] + * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"] + * </pre> + * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, {@code null} if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator, int max) { + return splitByWholeSeparatorWorker(str, separator, max, true); + } + + /** + * Performs the logic for the {@code splitByWholeSeparatorPreserveAllTokens} methods. + * + * @param str the String to parse, may be {@code null} + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + private static String[] splitByWholeSeparatorWorker( + String str, String separator, int max, boolean preserveAllTokens) { + if (str == null) { + return null; + } + + int len = str.length(); + + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + + if ((separator == null) || (EMPTY.equals(separator))) { + // Split on whitespace. + return splitWorker(str, null, max, preserveAllTokens); + } + + int separatorLength = separator.length(); + + ArrayList<String> substrings = new ArrayList<String>(); + int numberOfSubstrings = 0; + int beg = 0; + int end = 0; + while (end < len) { + end = str.indexOf(separator, beg); + + if (end > -1) { + if (end > beg) { + numberOfSubstrings += 1; + + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + // The following is OK, because String.substring( beg, end ) excludes + // the character at the position 'end'. + substrings.add(str.substring(beg, end)); + + // Set the starting point for the next search. + // The following is equivalent to beg = end + (separatorLength - 1) + 1, + // which is the right calculation: + beg = end + separatorLength; + } + } else { + // We found a consecutive occurrence of the separator, so skip it. + if (preserveAllTokens) { + numberOfSubstrings += 1; + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + substrings.add(EMPTY); + } + } + beg = end + separatorLength; + } + } else { + // String.substring( beg ) goes from 'beg' to the end of the String. + substrings.add(str.substring(beg)); + end = len; + } + } + + return substrings.toArray(new String[substrings.size()]); + } + + // ----------------------------------------------------------------------- + /** + * <p>Splits the provided text into an array, using whitespace as the + * separator, preserving all tokens, including empty tokens created by + * adjacent separators. This is an alternative to using StringTokenizer. + * Whitespace is defined by {@link Character#isWhitespace(char)}.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.splitPreserveAllTokens(null) = null + * StringUtils.splitPreserveAllTokens("") = [] + * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "def"] + * StringUtils.splitPreserveAllTokens("abc def") = ["abc", "", "def"] + * StringUtils.splitPreserveAllTokens(" abc ") = ["", "abc", ""] + * </pre> + * + * @param str the String to parse, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str) { + return splitWorker(str, null, -1, true); + } + + /** + * <p>Splits the provided text into an array, separator specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.splitPreserveAllTokens(null, *) = null + * StringUtils.splitPreserveAllTokens("", *) = [] + * StringUtils.splitPreserveAllTokens("a.b.c", '.') = ["a", "b", "c"] + * StringUtils.splitPreserveAllTokens("a..b.c", '.') = ["a", "", "b", "c"] + * StringUtils.splitPreserveAllTokens("a:b:c", '.') = ["a:b:c"] + * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"] + * StringUtils.splitPreserveAllTokens("a b c", ' ') = ["a", "b", "c"] + * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", ""] + * StringUtils.splitPreserveAllTokens("a b c ", ' ') = ["a", "b", "c", "", ""] + * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", a", "b", "c"] + * StringUtils.splitPreserveAllTokens(" a b c", ' ') = ["", "", a", "b", "c"] + * StringUtils.splitPreserveAllTokens(" a b c ", ' ') = ["", a", "b", "c", ""] + * </pre> + * + * @param str the String to parse, may be {@code null} + * @param separatorChar the character used as the delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, char separatorChar) { + return splitWorker(str, separatorChar, true); + } + + /** + * Performs the logic for the {@code split} and + * {@code splitPreserveAllTokens} methods that do not return a + * maximum array length. + * + * @param str the String to parse, may be {@code null} + * @param separatorChar the separate character + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + */ + private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) { + // Performance tuned for 2.0 (JDK1.4) + + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + List<String> list = new ArrayList<String>(); + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + while (i < len) { + if (str.charAt(i) == separatorChar) { + if (match || preserveAllTokens) { + list.add(str.substring(start, i)); + match = false; + lastMatch = true; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + if (match || (preserveAllTokens && lastMatch)) { + list.add(str.substring(start, i)); + } + return list.toArray(new String[list.size()]); + } + + /** + * <p>Splits the provided text into an array, separators specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.</p> + * + * <pre> + * StringUtils.splitPreserveAllTokens(null, *) = null + * StringUtils.splitPreserveAllTokens("", *) = [] + * StringUtils.splitPreserveAllTokens("abc def", null) = ["abc", "def"] + * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "def"] + * StringUtils.splitPreserveAllTokens("abc def", " ") = ["abc", "", def"] + * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":") = ["ab", "cd", "ef"] + * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":") = ["ab", "cd", "ef", ""] + * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""] + * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":") = ["ab", "", cd", "ef"] + * StringUtils.splitPreserveAllTokens(":cd:ef", ":") = ["", cd", "ef"] + * StringUtils.splitPreserveAllTokens("::cd:ef", ":") = ["", "", cd", "ef"] + * StringUtils.splitPreserveAllTokens(":cd:ef:", ":") = ["", cd", "ef", ""] + * </pre> + * + * @param str the String to parse, may be {@code null} + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, true); + } + + /** + * <p>Splits the provided text into an array with a maximum length, + * separators specified, preserving all tokens, including empty tokens + * created by adjacent separators.</p> + * + * <p>The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * Adjacent separators are treated as one separator.</p> + * + * <p>A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.</p> + * + * <p>If more than {@code max} delimited substrings are found, the last + * returned string includes all characters after the first {@code max - 1} + * returned strings (including separator characters).</p> + * + * <pre> + * StringUtils.splitPreserveAllTokens(null, *, *) = null + * StringUtils.splitPreserveAllTokens("", *, *) = [] + * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"] + * StringUtils.splitPreserveAllTokens("ab de fg", null, 0) = ["ab", "cd", "ef"] + * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0) = ["ab", "cd", "ef"] + * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2) = ["ab", "cd:ef"] + * StringUtils.splitPreserveAllTokens("ab de fg", null, 2) = ["ab", " de fg"] + * StringUtils.splitPreserveAllTokens("ab de fg", null, 3) = ["ab", "", " de fg"] + * StringUtils.splitPreserveAllTokens("ab de fg", null, 4) = ["ab", "", "", "de fg"] + * </pre> + * + * @param str the String to parse, may be {@code null} + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, true); + } + + /** + * Performs the logic for the {@code split} and + * {@code splitPreserveAllTokens} methods that return a maximum array + * length. + * + * @param str the String to parse, may be {@code null} + * @param separatorChars the separate character + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + */ + private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { + // Performance tuned for 2.0 (JDK1.4) + // Direct code is quicker than StringTokenizer. + // Also, StringTokenizer uses isSpace() not isWhitespace() + + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + List<String> list = new ArrayList<String>(); + int sizePlus1 = 1; + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + if (separatorChars == null) { + // Null separator means use whitespace + while (i < len) { + if (Character.isWhitespace(str.charAt(i))) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else if (separatorChars.length() == 1) { + // Optimise 1 character case + char sep = separatorChars.charAt(0); + while (i < len) { + if (str.charAt(i) == sep) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else { + // standard case + while (i < len) { + if (separatorChars.indexOf(str.charAt(i)) >= 0) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } + if (match || (preserveAllTokens && lastMatch)) { + list.add(str.substring(start, i)); + } + return list.toArray(new String[list.size()]); + } + + /** + * <p>Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens. + * <pre> + * StringUtils.splitByCharacterType(null) = null + * StringUtils.splitByCharacterType("") = [] + * StringUtils.splitByCharacterType("ab de fg") = ["ab", " ", "de", " ", "fg"] + * StringUtils.splitByCharacterType("ab de fg") = ["ab", " ", "de", " ", "fg"] + * StringUtils.splitByCharacterType("ab:cd:ef") = ["ab", ":", "cd", ":", "ef"] + * StringUtils.splitByCharacterType("number5") = ["number", "5"] + * StringUtils.splitByCharacterType("fooBar") = ["foo", "B", "ar"] + * StringUtils.splitByCharacterType("foo200Bar") = ["foo", "200", "B", "ar"] + * StringUtils.splitByCharacterType("ASFRules") = ["ASFR", "ules"] + * </pre> + * @param str the String to split, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + public static String[] splitByCharacterType(String str) { + return splitByCharacterType(str, false); + } + + /** + * <p>Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: the character of type + * {@code Character.UPPERCASE_LETTER}, if any, immediately + * preceding a token of type {@code Character.LOWERCASE_LETTER} + * will belong to the following token rather than to the preceding, if any, + * {@code Character.UPPERCASE_LETTER} token. + * <pre> + * StringUtils.splitByCharacterTypeCamelCase(null) = null + * StringUtils.splitByCharacterTypeCamelCase("") = [] + * StringUtils.splitByCharacterTypeCamelCase("ab de fg") = ["ab", " ", "de", " ", "fg"] + * StringUtils.splitByCharacterTypeCamelCase("ab de fg") = ["ab", " ", "de", " ", "fg"] + * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef") = ["ab", ":", "cd", ":", "ef"] + * StringUtils.splitByCharacterTypeCamelCase("number5") = ["number", "5"] + * StringUtils.splitByCharacterTypeCamelCase("fooBar") = ["foo", "Bar"] + * StringUtils.splitByCharacterTypeCamelCase("foo200Bar") = ["foo", "200", "Bar"] + * StringUtils.splitByCharacterTypeCamelCase("ASFRules") = ["ASF", "Rules"] + * </pre> + * @param str the String to split, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + public static String[] splitByCharacterTypeCamelCase(String str) { + return splitByCharacterType(str, true); + } + + /** + * <p>Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: if {@code camelCase} is {@code true}, + * the character of type {@code Character.UPPERCASE_LETTER}, if any, + * immediately preceding a token of type {@code Character.LOWERCASE_LETTER} + * will belong to the following token rather than to the preceding, if any, + * {@code Character.UPPERCASE_LETTER} token. + * @param str the String to split, may be {@code null} + * @param camelCase whether to use so-called "camel-case" for letter types + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + private static String[] splitByCharacterType(String str, boolean camelCase) { + if (str == null) { + return null; + } + if (str.length() == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + char[] c = str.toCharArray(); + List<String> list = new ArrayList<String>(); + int tokenStart = 0; + int currentType = Character.getType(c[tokenStart]); + for (int pos = tokenStart + 1; pos < c.length; pos++) { + int type = Character.getType(c[pos]); + if (type == currentType) { + continue; + } + if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) { + int newTokenStart = pos - 1; + if (newTokenStart != tokenStart) { + list.add(new String(c, tokenStart, newTokenStart - tokenStart)); + tokenStart = newTokenStart; + } + } else { + list.add(new String(c, tokenStart, pos - tokenStart)); + tokenStart = pos; + } + currentType = type; + } + list.add(new String(c, tokenStart, c.length - tokenStart)); + return list.toArray(new String[list.size()]); + } + + // Joining + //----------------------------------------------------------------------- + /** + * <p>Joins the elements of the provided array into a single String + * containing the provided list of elements.</p> + * + * <p>No separator is added to the joined String. + * Null objects or empty strings within the array are represented by + * empty strings.</p> + * + * <pre> + * StringUtils.join(null) = null + * StringUtils.join([]) = "" + * StringUtils.join([null]) = "" + * StringUtils.join(["a", "b", "c"]) = "abc" + * StringUtils.join([null, "", "a"]) = "a" + * </pre> + * + * @param <T> the specific type of values to join together + * @param elements the values to join together, may be null + * @return the joined String, {@code null} if null array input + * @since 2.0 + * @since 3.0 Changed signature to use varargs + */ + public static <T> String join(T... elements) { + return join(elements, null); + } + + /** + * <p>Joins the elements of the provided array into a single String + * containing the provided list of elements.</p> + * + * <p>No delimiter is added before or after the list. + * Null objects or empty strings within the array are represented by + * empty strings.</p> + * + * <pre> + * StringUtils.join(null, *) = null + * StringUtils.join([], *) = "" + * StringUtils.join([null], *) = "" + * StringUtils.join(["a", "b", "c"], ';') = "a;b;c" + * StringUtils.join(["a", "b", "c"], null) = "abc" + * StringUtils.join([null, "", "a"], ';') = ";;a" + * </pre> + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null array input + * @since 2.0 + */ + public static String join(Object[] array, char separator) { + if (array == null) { + return null; + } + + return join(array, separator, 0, array.length); + } + + /** + * <p>Joins the elements of the provided array into a single String + * containing the provided list of elements.</p> + * + * <p>No delimiter is added before or after the list. + * Null objects or empty strings within the array are represented by + * empty strings.</p> + * + * <pre> + * StringUtils.join(null, *) = null + * StringUtils.join([], *) = "" + * StringUtils.join([null], *) = "" + * StringUtils.join(["a", "b", "c"], ';') = "a;b;c" + * StringUtils.join(["a", "b", "c"], null) = "abc" + * StringUtils.join([null, "", "a"], ';') = ";;a" + * </pre> + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, {@code null} if null array input + * @since 2.0 + */ + public static String join(Object[] array, char separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + int noOfItems = (endIndex - startIndex); + if (noOfItems <= 0) { + return EMPTY; + } + + StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + /** + * <p>Joins the elements of the provided array into a single String + * containing the provided list of elements.</p> + * + * <p>No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.</p> + * + * <pre> + * StringUtils.join(null, *) = null + * StringUtils.join([], *) = "" + * StringUtils.join([null], *) = "" + * StringUtils.join(["a", "b", "c"], "--") = "a--b--c" + * StringUtils.join(["a", "b", "c"], null) = "abc" + * StringUtils.join(["a", "b", "c"], "") = "abc" + * StringUtils.join([null, "", "a"], ',') = ",,a" + * </pre> + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null array input + */ + public static String join(Object[] array, String separator) { + if (array == null) { + return null; + } + return join(array, separator, 0, array.length); + } + + /** + * <p>Joins the elements of the provided array into a single String + * containing the provided list of elements.</p> + * + * <p>No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.</p> + * + * <pre> + * StringUtils.join(null, *) = null + * StringUtils.join([], *) = "" + * StringUtils.join([null], *) = "" + * StringUtils.join(["a", "b", "c"], "--") = "a--b--c" + * StringUtils.join(["a", "b", "c"], null) = "abc" + * StringUtils.join(["a", "b", "c"], "") = "abc" + * StringUtils.join([null, "", "a"], ',') = ",,a" + * </pre> + * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, {@code null} if null array input + */ + public static String join(Object[] array, String separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + if (separator == null) { + separator = EMPTY; + } + + // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) + // (Assuming that all Strings are roughly equally long) + int noOfItems = (endIndex - startIndex); + if (noOfItems <= 0) { + return EMPTY; + } + + StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + /** + * <p>Joins the elements of the provided {@code Iterator} into + * a single String containing the provided elements.</p> + * + * <p>No delimiter is added before or after the list. Null objects or empty + * strings within the iteration are represented by empty strings.</p> + * + * <p>See the examples here: {@link #join(Object[],char)}. </p> + * + * @param iterator the {@code Iterator} of values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null iterator input + * @since 2.0 + */ + public static String join(Iterator<?> iterator, char separator) { + + // handle null, zero and one elements before building a buffer + if (iterator == null) { + return null; + } + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + + while (iterator.hasNext()) { + buf.append(separator); + Object obj = iterator.next(); + if (obj != null) { + buf.append(obj); + } + } + + return buf.toString(); + } + + /** + * <p>Joins the elements of the provided {@code Iterator} into + * a single String containing the provided elements.</p> + * + * <p>No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String ("").</p> + * + * <p>See the examples here: {@link #join(Object[],String)}. </p> + * + * @param iterator the {@code Iterator} of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null iterator input + */ + public static String join(Iterator<?> iterator, String separator) { + + // handle null, zero and one elements before building a buffer + if (iterator == null) { + return null; + } + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + + while (iterator.hasNext()) { + if (separator != null) { + buf.append(separator); + } + Object obj = iterator.next(); + if (obj != null) { + buf.append(obj); + } + } + return buf.toString(); + } + + /** + * <p>Joins the elements of the provided {@code Iterable} into + * a single String containing the provided elements.</p> + * + * <p>No delimiter is added before or after the list. Null objects or empty + * strings within the iteration are represented by empty strings.</p> + * + * <p>See the examples here: {@link #join(Object[],char)}. </p> + * + * @param iterable the {@code Iterable} providing the values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null iterator input + * @since 2.3 + */ + public static String join(Iterable<?> iterable, char separator) { + if (iterable == null) { + return null; + } + return join(iterable.iterator(), separator); + } + + /** + * <p>Joins the elements of the provided {@code Iterable} into + * a single String containing the provided elements.</p> + * + * <p>No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String ("").</p> + * + * <p>See the examples here: {@link #join(Object[],String)}. </p> + * + * @param iterable the {@code Iterable} providing the values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null iterator input + * @since 2.3 + */ + public static String join(Iterable<?> iterable, String separator) { + if (iterable == null) { + return null; + } + return join(iterable.iterator(), separator); + } + + // Delete + //----------------------------------------------------------------------- + /** + * <p>Deletes all whitespaces from a String as defined by + * {@link Character#isWhitespace(char)}.</p> + * + * <pre> + * StringUtils.deleteWhitespace(null) = null + * StringUtils.deleteWhitespace("") = "" + * StringUtils.deleteWhitespace("abc") = "abc" + * StringUtils.deleteWhitespace(" ab c ") = "abc" + * </pre> + * + * @param str the String to delete whitespace from, may be null + * @return the String without whitespaces, {@code null} if null String input + */ + public static String deleteWhitespace(String str) { + if (isEmpty(str)) { + return str; + } + int sz = str.length(); + char[] chs = new char[sz]; + int count = 0; + for (int i = 0; i < sz; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + chs[count++] = str.charAt(i); + } + } + if (count == sz) { + return str; + } + return new String(chs, 0, count); + } + + // Remove + //----------------------------------------------------------------------- + /** + * <p>Removes a substring only if it is at the beginning of a source string, + * otherwise returns the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.</p> + * + * <pre> + * StringUtils.removeStart(null, *) = null + * StringUtils.removeStart("", *) = "" + * StringUtils.removeStart(*, null) = * + * StringUtils.removeStart("www.domain.com", "www.") = "domain.com" + * StringUtils.removeStart("domain.com", "www.") = "domain.com" + * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com" + * StringUtils.removeStart("abc", "") = "abc" + * </pre> + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String removeStart(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.startsWith(remove)){ + return str.substring(remove.length()); + } + return str; + } + + /** + * <p>Case insensitive removal of a substring if it is at the beginning of a source string, + * otherwise returns the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.</p> + * + * <pre> + * StringUtils.removeStartIgnoreCase(null, *) = null + * StringUtils.removeStartIgnoreCase("", *) = "" + * StringUtils.removeStartIgnoreCase(*, null) = * + * StringUtils.removeStartIgnoreCase("www.domain.com", "www.") = "domain.com" + * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.") = "domain.com" + * StringUtils.removeStartIgnoreCase("domain.com", "www.") = "domain.com" + * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com" + * StringUtils.removeStartIgnoreCase("abc", "") = "abc" + * </pre> + * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.4 + */ + public static String removeStartIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (startsWithIgnoreCase(str, remove)) { + return str.substring(remove.length()); + } + return str; + } + + /** + * <p>Removes a substring only if it is at the end of a source string, + * otherwise returns the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.</p> + * + * <pre> + * StringUtils.removeEnd(null, *) = null + * StringUtils.removeEnd("", *) = "" + * StringUtils.removeEnd(*, null) = * + * StringUtils.removeEnd("www.domain.com", ".com.") = "www.domain.com" + * StringUtils.removeEnd("www.domain.com", ".com") = "www.domain" + * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com" + * StringUtils.removeEnd("abc", "") = "abc" + * </pre> + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String removeEnd(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.endsWith(remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + * <p>Case insensitive removal of a substring if it is at the end of a source string, + * otherwise returns the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.</p> + * + * <pre> + * StringUtils.removeEndIgnoreCase(null, *) = null + * StringUtils.removeEndIgnoreCase("", *) = "" + * StringUtils.removeEndIgnoreCase(*, null) = * + * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.") = "www.domain.com" + * StringUtils.removeEndIgnoreCase("www.domain.com", ".com") = "www.domain" + * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com" + * StringUtils.removeEndIgnoreCase("abc", "") = "abc" + * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain") + * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain") + * </pre> + * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.4 + */ + public static String removeEndIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (endsWithIgnoreCase(str, remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + * <p>Removes all occurrences of a substring from within the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} remove string will return the source string. + * An empty ("") remove string will return the source string.</p> + * + * <pre> + * StringUtils.remove(null, *) = null + * StringUtils.remove("", *) = "" + * StringUtils.remove(*, null) = * + * StringUtils.remove(*, "") = * + * StringUtils.remove("queued", "ue") = "qd" + * StringUtils.remove("queued", "zz") = "queued" + * </pre> + * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String remove(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + return replace(str, remove, EMPTY, -1); + } + + /** + * <p>Removes all occurrences of a character from within the source string.</p> + * + * <p>A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string.</p> + * + * <pre> + * StringUtils.remove(null, *) = null + * StringUtils.remove("", *) = "" + * StringUtils.remove("queued", 'u') = "qeed" + * StringUtils.remove("queued", 'z') = "queued" + * </pre> + * + * @param str the source String to search, may be null + * @param remove the char to search for and remove, may be null + * @return the substring with the char removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String remove(String str, char remove) { + if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) { + return str; + } + char[] chars = str.toCharArray(); + int pos = 0; + for (int i = 0; i < chars.length; i++) { + if (chars[i] != remove) { + chars[pos++] = chars[i]; + } + } + return new String(chars, 0, pos); + } + + // Replacing + //----------------------------------------------------------------------- + /** + * <p>Replaces a String with another String inside a larger String, once.</p> + * + * <p>A {@code null} reference passed to this method is a no-op.</p> + * + * <pre> + * StringUtils.replaceOnce(null, *, *) = null + * StringUtils.replaceOnce("", *, *) = "" + * StringUtils.replaceOnce("any", null, *) = "any" + * StringUtils.replaceOnce("any", *, null) = "any" + * StringUtils.replaceOnce("any", "", *) = "any" + * StringUtils.replaceOnce("aba", "a", null) = "aba" + * StringUtils.replaceOnce("aba", "a", "") = "ba" + * StringUtils.replaceOnce("aba", "a", "z") = "zba" + * </pre> + * + * @see #replace(String text, String searchString, String replacement, int max) + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace with, may be null + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replaceOnce(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, 1); + } + + /** + * <p>Replaces all occurrences of a String within another String.</p> + * + * <p>A {@code null} reference passed to this method is a no-op.</p> + * + * <pre> + * StringUtils.replace(null, *, *) = null + * StringUtils.replace("", *, *) = "" + * StringUtils.replace("any", null, *) = "any" + * StringUtils.replace("any", *, null) = "any" + * StringUtils.replace("any", "", *) = "any" + * StringUtils.replace("aba", "a", null) = "aba" + * StringUtils.replace("aba", "a", "") = "b" + * StringUtils.replace("aba", "a", "z") = "zbz" + * </pre> + * + * @see #replace(String text, String searchString, String replacement, int max) + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replace(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, -1); + } + + /** + * <p>Replaces a String with another String inside a larger String, + * for the first {@code max} values of the search String.</p> + * + * <p>A {@code null} reference passed to this method is a no-op.</p> + * + * <pre> + * StringUtils.replace(null, *, *, *) = null + * StringUtils.replace("", *, *, *) = "" + * StringUtils.replace("any", null, *, *) = "any" + * StringUtils.replace("any", *, null, *) = "any" + * StringUtils.replace("any", "", *, *) = "any" + * StringUtils.replace("any", *, *, 0) = "any" + * StringUtils.replace("abaa", "a", null, -1) = "abaa" + * StringUtils.replace("abaa", "a", "", -1) = "b" + * StringUtils.replace("abaa", "a", "z", 0) = "abaa" + * StringUtils.replace("abaa", "a", "z", 1) = "zbaa" + * StringUtils.replace("abaa", "a", "z", 2) = "zbza" + * StringUtils.replace("abaa", "a", "z", -1) = "zbzz" + * </pre> + * + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @param max maximum number of values to replace, or {@code -1} if no maximum + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replace(String text, String searchString, String replacement, int max) { + if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) { + return text; + } + int start = 0; + int end = text.indexOf(searchString, start); + if (end == INDEX_NOT_FOUND) { + return text; + } + int replLength = searchString.length(); + int increase = replacement.length() - replLength; + increase = (increase < 0 ? 0 : increase); + increase *= (max < 0 ? 16 : (max > 64 ? 64 : max)); + StringBuilder buf = new StringBuilder(text.length() + increase); + while (end != INDEX_NOT_FOUND) { + buf.append(text.substring(start, end)).append(replacement); + start = end + replLength; + if (--max == 0) { + break; + } + end = text.indexOf(searchString, start); + } + buf.append(text.substring(start)); + return buf.toString(); + } + + /** + * <p> + * Replaces all occurrences of Strings within another String. + * </p> + * + * <p> + * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. This will not repeat. For repeating replaces, call the + * overloaded method. + * </p> + * + * <pre> + * StringUtils.replaceEach(null, *, *) = null + * StringUtils.replaceEach("", *, *) = "" + * StringUtils.replaceEach("aba", null, null) = "aba" + * StringUtils.replaceEach("aba", new String[0], null) = "aba" + * StringUtils.replaceEach("aba", null, new String[0]) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, null) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}) = "b" + * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}) = "aba" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}) = "wcte" + * (example of how it does not repeat) + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}) = "dcte" + * </pre> + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEach(String text, String[] searchList, String[] replacementList) { + return replaceEach(text, searchList, replacementList, false, 0); + } + + /** + * <p> + * Replaces all occurrences of Strings within another String. + * </p> + * + * <p> + * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. + * </p> + * + * <pre> + * StringUtils.replaceEach(null, *, *, *) = null + * StringUtils.replaceEach("", *, *, *) = "" + * StringUtils.replaceEach("aba", null, null, *) = "aba" + * StringUtils.replaceEach("aba", new String[0], null, *) = "aba" + * StringUtils.replaceEach("aba", null, new String[0], *) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b" + * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte" + * (example of how it repeats) + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalStateException + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe" + * </pre> + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalStateException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) { + // timeToLive should be 0 if not used or nothing to replace, else it's + // the length of the replace array + int timeToLive = searchList == null ? 0 : searchList.length; + return replaceEach(text, searchList, replacementList, true, timeToLive); + } + + /** + * <p> + * Replaces all occurrences of Strings within another String. + * </p> + * + * <p> + * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. + * </p> + * + * <pre> + * StringUtils.replaceEach(null, *, *, *) = null + * StringUtils.replaceEach("", *, *, *) = "" + * StringUtils.replaceEach("aba", null, null, *) = "aba" + * StringUtils.replaceEach("aba", new String[0], null, *) = "aba" + * StringUtils.replaceEach("aba", null, new String[0], *) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba" + * StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b" + * StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte" + * (example of how it repeats) + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte" + * StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException + * </pre> + * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @param repeat if true, then replace repeatedly + * until there are no more possible replacements or timeToLive < 0 + * @param timeToLive + * if less than 0 then there is a circular reference and endless + * loop + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalStateException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + private static String replaceEach( + String text, String[] searchList, String[] replacementList, boolean repeat, int timeToLive) { + + // mchyzer Performance note: This creates very few new objects (one major goal) + // let me know if there are performance requests, we can create a harness to measure + + if (text == null || text.length() == 0 || searchList == null || + searchList.length == 0 || replacementList == null || replacementList.length == 0) { + return text; + } + + // if recursing, this shouldn't be less than 0 + if (timeToLive < 0) { + throw new IllegalStateException("Aborting to protect against StackOverflowError - " + + "output of one loop is the input of another"); + } + + int searchLength = searchList.length; + int replacementLength = replacementList.length; + + // make sure lengths are ok, these need to be equal + if (searchLength != replacementLength) { + throw new IllegalArgumentException("Search and Replace array lengths don't match: " + + searchLength + + " vs " + + replacementLength); + } + + // keep track of which still have matches + boolean[] noMoreMatchesForReplIndex = new boolean[searchLength]; + + // index on index that the match was found + int textIndex = -1; + int replaceIndex = -1; + int tempIndex = -1; + + // index of replace array that will replace the search string found + // NOTE: logic duplicated below START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) { + continue; + } + tempIndex = text.indexOf(searchList[i]); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic mostly below END + + // no search strings found, we are done + if (textIndex == -1) { + return text; + } + + int start = 0; + + // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit + int increase = 0; + + // count the replacement text elements that are larger than their corresponding text being replaced + for (int i = 0; i < searchList.length; i++) { + if (searchList[i] == null || replacementList[i] == null) { + continue; + } + int greater = replacementList[i].length() - searchList[i].length(); + if (greater > 0) { + increase += 3 * greater; // assume 3 matches + } + } + // have upper-bound at 20% increase, then let Java take over + increase = Math.min(increase, text.length() / 5); + + StringBuilder buf = new StringBuilder(text.length() + increase); + + while (textIndex != -1) { + + for (int i = start; i < textIndex; i++) { + buf.append(text.charAt(i)); + } + buf.append(replacementList[replaceIndex]); + + start = textIndex + searchList[replaceIndex].length(); + + textIndex = -1; + replaceIndex = -1; + tempIndex = -1; + // find the next earliest match + // NOTE: logic mostly duplicated above START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) { + continue; + } + tempIndex = text.indexOf(searchList[i], start); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic duplicated above END + + } + int textLength = text.length(); + for (int i = start; i < textLength; i++) { + buf.append(text.charAt(i)); + } + String result = buf.toString(); + if (!repeat) { + return result; + } + + return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1); + } + + // Replace, character based + //----------------------------------------------------------------------- + /** + * <p>Replaces all occurrences of a character in a String with another. + * This is a null-safe version of {@link String#replace(char, char)}.</p> + * + * <p>A {@code null} string input returns {@code null}. + * An empty ("") string input returns an empty string.</p> + * + * <pre> + * StringUtils.replaceChars(null, *, *) = null + * StringUtils.replaceChars("", *, *) = "" + * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya" + * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba" + * </pre> + * + * @param str String to replace characters in, may be null + * @param searchChar the character to search for, may be null + * @param replaceChar the character to replace, may be null + * @return modified String, {@code null} if null string input + * @since 2.0 + */ + public static String replaceChars(String str, char searchChar, char replaceChar) { + if (str == null) { + return null; + } + return str.replace(searchChar, replaceChar); + } + + /** + * <p>Replaces multiple characters in a String in one go. + * This method can also be used to delete characters.</p> + * + * <p>For example:<br /> + * <code>replaceChars("hello", "ho", "jy") = jelly</code>.</p> + * + * <p>A {@code null} string input returns {@code null}. + * An empty ("") string input returns an empty string. + * A null or empty set of search characters returns the input string.</p> + * + * <p>The length of the search characters should normally equal the length + * of the replace characters. + * If the search characters is longer, then the extra search characters + * are deleted. + * If the search characters is shorter, then the extra replace characters + * are ignored.</p> + * + * <pre> + * StringUtils.replaceChars(null, *, *) = null + * StringUtils.replaceChars("", *, *) = "" + * StringUtils.replaceChars("abc", null, *) = "abc" + * StringUtils.replaceChars("abc", "", *) = "abc" + * StringUtils.replaceChars("abc", "b", null) = "ac" + * StringUtils.replaceChars("abc", "b", "") = "ac" + * StringUtils.replaceChars("abcba", "bc", "yz") = "ayzya" + * StringUtils.replaceChars("abcba", "bc", "y") = "ayya" + * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya" + * </pre> + * + * @param str String to replace characters in, may be null + * @param searchChars a set of characters to search for, may be null + * @param replaceChars a set of characters to replace, may be null + * @return modified String, {@code null} if null string input + * @since 2.0 + */ + public static String replaceChars(String str, String searchChars, String replaceChars) { + if (isEmpty(str) || isEmpty(searchChars)) { + return str; + } + if (replaceChars == null) { + replaceChars = EMPTY; + } + boolean modified = false; + int replaceCharsLength = replaceChars.length(); + int strLength = str.length(); + StringBuilder buf = new StringBuilder(strLength); + for (int i = 0; i < strLength; i++) { + char ch = str.charAt(i); + int index = searchChars.indexOf(ch); + if (index >= 0) { + modified = true; + if (index < replaceCharsLength) { + buf.append(replaceChars.charAt(index)); + } + } else { + buf.append(ch); + } + } + if (modified) { + return buf.toString(); + } + return str; + } + + // Overlay + //----------------------------------------------------------------------- + /** + * <p>Overlays part of a String with another String.</p> + * + * <p>A {@code null} string input returns {@code null}. + * A negative index is treated as zero. + * An index greater than the string length is treated as the string length. + * The start index is always the smaller of the two indices.</p> + * + * <pre> + * StringUtils.overlay(null, *, *, *) = null + * StringUtils.overlay("", "abc", 0, 0) = "abc" + * StringUtils.overlay("abcdef", null, 2, 4) = "abef" + * StringUtils.overlay("abcdef", "", 2, 4) = "abef" + * StringUtils.overlay("abcdef", "", 4, 2) = "abef" + * StringUtils.overlay("abcdef", "zzzz", 2, 4) = "abzzzzef" + * StringUtils.overlay("abcdef", "zzzz", 4, 2) = "abzzzzef" + * StringUtils.overlay("abcdef", "zzzz", -1, 4) = "zzzzef" + * StringUtils.overlay("abcdef", "zzzz", 2, 8) = "abzzzz" + * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef" + * StringUtils.overlay("abcdef", "zzzz", 8, 10) = "abcdefzzzz" + * </pre> + * + * @param str the String to do overlaying in, may be null + * @param overlay the String to overlay, may be null + * @param start the position to start overlaying at + * @param end the position to stop overlaying before + * @return overlayed String, {@code null} if null String input + * @since 2.0 + */ + public static String overlay(String str, String overlay, int start, int end) { + if (str == null) { + return null; + } + if (overlay == null) { + overlay = EMPTY; + } + int len = str.length(); + if (start < 0) { + start = 0; + } + if (start > len) { + start = len; + } + if (end < 0) { + end = 0; + } + if (end > len) { + end = len; + } + if (start > end) { + int temp = start; + start = end; + end = temp; + } + return new StringBuilder(len + start - end + overlay.length() + 1) + .append(str.substring(0, start)) + .append(overlay) + .append(str.substring(end)) + .toString(); + } + + // Chomping + //----------------------------------------------------------------------- + /** + * <p>Removes one newline from end of a String if it's there, + * otherwise leave it alone. A newline is "{@code \n}", + * "{@code \r}", or "{@code \r\n}".</p> + * + * <p>NOTE: This method changed in 2.0. + * It now more closely matches Perl chomp.</p> + * + * <pre> + * StringUtils.chomp(null) = null + * StringUtils.chomp("") = "" + * StringUtils.chomp("abc \r") = "abc " + * StringUtils.chomp("abc\n") = "abc" + * StringUtils.chomp("abc\r\n") = "abc" + * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n" + * StringUtils.chomp("abc\n\r") = "abc\n" + * StringUtils.chomp("abc\n\rabc") = "abc\n\rabc" + * StringUtils.chomp("\r") = "" + * StringUtils.chomp("\n") = "" + * StringUtils.chomp("\r\n") = "" + * </pre> + * + * @param str the String to chomp a newline from, may be null + * @return String without newline, {@code null} if null String input + */ + public static String chomp(String str) { + if (isEmpty(str)) { + return str; + } + + if (str.length() == 1) { + char ch = str.charAt(0); + if (ch == CharUtils.CR || ch == CharUtils.LF) { + return EMPTY; + } + return str; + } + + int lastIdx = str.length() - 1; + char last = str.charAt(lastIdx); + + if (last == CharUtils.LF) { + if (str.charAt(lastIdx - 1) == CharUtils.CR) { + lastIdx--; + } + } else if (last != CharUtils.CR) { + lastIdx++; + } + return str.substring(0, lastIdx); + } + + // Chopping + //----------------------------------------------------------------------- + /** + * <p>Remove the last character from a String.</p> + * + * <p>If the String ends in {@code \r\n}, then remove both + * of them.</p> + * + * <pre> + * StringUtils.chop(null) = null + * StringUtils.chop("") = "" + * StringUtils.chop("abc \r") = "abc " + * StringUtils.chop("abc\n") = "abc" + * StringUtils.chop("abc\r\n") = "abc" + * StringUtils.chop("abc") = "ab" + * StringUtils.chop("abc\nabc") = "abc\nab" + * StringUtils.chop("a") = "" + * StringUtils.chop("\r") = "" + * StringUtils.chop("\n") = "" + * StringUtils.chop("\r\n") = "" + * </pre> + * + * @param str the String to chop last character from, may be null + * @return String without last character, {@code null} if null String input + */ + public static String chop(String str) { + if (str == null) { + return null; + } + int strLen = str.length(); + if (strLen < 2) { + return EMPTY; + } + int lastIdx = strLen - 1; + String ret = str.substring(0, lastIdx); + char last = str.charAt(lastIdx); + if (last == CharUtils.LF && ret.charAt(lastIdx - 1) == CharUtils.CR) { + return ret.substring(0, lastIdx - 1); + } + return ret; + } + + // Conversion + //----------------------------------------------------------------------- + + // Padding + //----------------------------------------------------------------------- + /** + * <p>Repeat a String {@code repeat} times to form a + * new String.</p> + * + * <pre> + * StringUtils.repeat(null, 2) = null + * StringUtils.repeat("", 0) = "" + * StringUtils.repeat("", 2) = "" + * StringUtils.repeat("a", 3) = "aaa" + * StringUtils.repeat("ab", 2) = "abab" + * StringUtils.repeat("a", -2) = "" + * </pre> + * + * @param str the String to repeat, may be null + * @param repeat number of times to repeat str, negative treated as zero + * @return a new String consisting of the original String repeated, + * {@code null} if null String input + */ + public static String repeat(String str, int repeat) { + // Performance tuned for 2.0 (JDK1.4) + + if (str == null) { + return null; + } + if (repeat <= 0) { + return EMPTY; + } + int inputLength = str.length(); + if (repeat == 1 || inputLength == 0) { + return str; + } + if (inputLength == 1 && repeat <= PAD_LIMIT) { + return repeat(str.charAt(0), repeat); + } + + int outputLength = inputLength * repeat; + switch (inputLength) { + case 1 : + return repeat(str.charAt(0), repeat); + case 2 : + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char[] output2 = new char[outputLength]; + for (int i = repeat * 2 - 2; i >= 0; i--, i--) { + output2[i] = ch0; + output2[i + 1] = ch1; + } + return new String(output2); + default : + StringBuilder buf = new StringBuilder(outputLength); + for (int i = 0; i < repeat; i++) { + buf.append(str); + } + return buf.toString(); + } + } + + /** + * <p>Repeat a String {@code repeat} times to form a + * new String, with a String separator injected each time. </p> + * + * <pre> + * StringUtils.repeat(null, null, 2) = null + * StringUtils.repeat(null, "x", 2) = null + * StringUtils.repeat("", null, 0) = "" + * StringUtils.repeat("", "", 2) = "" + * StringUtils.repeat("", "x", 3) = "xxx" + * StringUtils.repeat("?", ", ", 3) = "?, ?, ?" + * </pre> + * + * @param str the String to repeat, may be null + * @param separator the String to inject, may be null + * @param repeat number of times to repeat str, negative treated as zero + * @return a new String consisting of the original String repeated, + * {@code null} if null String input + * @since 2.5 + */ + public static String repeat(String str, String separator, int repeat) { + if(str == null || separator == null) { + return repeat(str, repeat); + } else { + // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it + String result = repeat(str + separator, repeat); + return removeEnd(result, separator); + } + } + + /** + * <p>Returns padding using the specified delimiter repeated + * to a given length.</p> + * + * <pre> + * StringUtils.repeat(0, 'e') = "" + * StringUtils.repeat(3, 'e') = "eee" + * StringUtils.repeat(-2, 'e') = "" + * </pre> + * + * <p>Note: this method doesn't not support padding with + * <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a> + * as they require a pair of {@code char}s to be represented. + * If you are needing to support full I18N of your applications + * consider using {@link #repeat(String, int)} instead. + * </p> + * + * @param ch character to repeat + * @param repeat number of times to repeat char, negative treated as zero + * @return String with repeated character + * @see #repeat(String, int) + */ + public static String repeat(char ch, int repeat) { + char[] buf = new char[repeat]; + for (int i = repeat - 1; i >= 0; i--) { + buf[i] = ch; + } + return new String(buf); + } + + /** + * <p>Right pad a String with spaces (' ').</p> + * + * <p>The String is padded to the size of {@code size}.</p> + * + * <pre> + * StringUtils.rightPad(null, *) = null + * StringUtils.rightPad("", 3) = " " + * StringUtils.rightPad("bat", 3) = "bat" + * StringUtils.rightPad("bat", 5) = "bat " + * StringUtils.rightPad("bat", 1) = "bat" + * StringUtils.rightPad("bat", -1) = "bat" + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String rightPad(String str, int size) { + return rightPad(str, size, ' '); + } + + /** + * <p>Right pad a String with a specified character.</p> + * + * <p>The String is padded to the size of {@code size}.</p> + * + * <pre> + * StringUtils.rightPad(null, *, *) = null + * StringUtils.rightPad("", 3, 'z') = "zzz" + * StringUtils.rightPad("bat", 3, 'z') = "bat" + * StringUtils.rightPad("bat", 5, 'z') = "batzz" + * StringUtils.rightPad("bat", 1, 'z') = "bat" + * StringUtils.rightPad("bat", -1, 'z') = "bat" + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + * @since 2.0 + */ + public static String rightPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return rightPad(str, size, String.valueOf(padChar)); + } + return str.concat(repeat(padChar, pads)); + } + + /** + * <p>Right pad a String with a specified String.</p> + * + * <p>The String is padded to the size of {@code size}.</p> + * + * <pre> + * StringUtils.rightPad(null, *, *) = null + * StringUtils.rightPad("", 3, "z") = "zzz" + * StringUtils.rightPad("bat", 3, "yz") = "bat" + * StringUtils.rightPad("bat", 5, "yz") = "batyz" + * StringUtils.rightPad("bat", 8, "yz") = "batyzyzy" + * StringUtils.rightPad("bat", 1, "yz") = "bat" + * StringUtils.rightPad("bat", -1, "yz") = "bat" + * StringUtils.rightPad("bat", 5, null) = "bat " + * StringUtils.rightPad("bat", 5, "") = "bat " + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String rightPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return rightPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return str.concat(padStr); + } else if (pads < padLen) { + return str.concat(padStr.substring(0, pads)); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return str.concat(new String(padding)); + } + } + + /** + * <p>Left pad a String with spaces (' ').</p> + * + * <p>The String is padded to the size of {@code size}.</p> + * + * <pre> + * StringUtils.leftPad(null, *) = null + * StringUtils.leftPad("", 3) = " " + * StringUtils.leftPad("bat", 3) = "bat" + * StringUtils.leftPad("bat", 5) = " bat" + * StringUtils.leftPad("bat", 1) = "bat" + * StringUtils.leftPad("bat", -1) = "bat" + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String leftPad(String str, int size) { + return leftPad(str, size, ' '); + } + + /** + * <p>Left pad a String with a specified character.</p> + * + * <p>Pad to a size of {@code size}.</p> + * + * <pre> + * StringUtils.leftPad(null, *, *) = null + * StringUtils.leftPad("", 3, 'z') = "zzz" + * StringUtils.leftPad("bat", 3, 'z') = "bat" + * StringUtils.leftPad("bat", 5, 'z') = "zzbat" + * StringUtils.leftPad("bat", 1, 'z') = "bat" + * StringUtils.leftPad("bat", -1, 'z') = "bat" + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + * @since 2.0 + */ + public static String leftPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return leftPad(str, size, String.valueOf(padChar)); + } + return repeat(padChar, pads).concat(str); + } + + /** + * <p>Left pad a String with a specified String.</p> + * + * <p>Pad to a size of {@code size}.</p> + * + * <pre> + * StringUtils.leftPad(null, *, *) = null + * StringUtils.leftPad("", 3, "z") = "zzz" + * StringUtils.leftPad("bat", 3, "yz") = "bat" + * StringUtils.leftPad("bat", 5, "yz") = "yzbat" + * StringUtils.leftPad("bat", 8, "yz") = "yzyzybat" + * StringUtils.leftPad("bat", 1, "yz") = "bat" + * StringUtils.leftPad("bat", -1, "yz") = "bat" + * StringUtils.leftPad("bat", 5, null) = " bat" + * StringUtils.leftPad("bat", 5, "") = " bat" + * </pre> + * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String leftPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return leftPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return padStr.concat(str); + } else if (pads < padLen) { + return padStr.substring(0, pads).concat(str); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return new String(padding).concat(str); + } + } + + /** + * Gets a CharSequence length or {@code 0} if the CharSequence is + * {@code null}. + * + * @param cs + * a CharSequence or {@code null} + * @return CharSequence length or {@code 0} if the CharSequence is + * {@code null}. + * @since 2.4 + * @since 3.0 Changed signature from length(String) to length(CharSequence) + */ + public static int length(CharSequence cs) { + return cs == null ? 0 : cs.length(); + } + + // Centering + //----------------------------------------------------------------------- + /** + * <p>Centers a String in a larger String of size {@code size} + * using the space character (' ').<p> + * + * <p>If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.</p> + * + * <p>Equivalent to {@code center(str, size, " ")}.</p> + * + * <pre> + * StringUtils.center(null, *) = null + * StringUtils.center("", 4) = " " + * StringUtils.center("ab", -1) = "ab" + * StringUtils.center("ab", 4) = " ab " + * StringUtils.center("abcd", 2) = "abcd" + * StringUtils.center("a", 4) = " a " + * </pre> + * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @return centered String, {@code null} if null String input + */ + public static String center(String str, int size) { + return center(str, size, ' '); + } + + /** + * <p>Centers a String in a larger String of size {@code size}. + * Uses a supplied character as the value to pad the String with.</p> + * + * <p>If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.</p> + * + * <pre> + * StringUtils.center(null, *, *) = null + * StringUtils.center("", 4, ' ') = " " + * StringUtils.center("ab", -1, ' ') = "ab" + * StringUtils.center("ab", 4, ' ') = " ab" + * StringUtils.center("abcd", 2, ' ') = "abcd" + * StringUtils.center("a", 4, ' ') = " a " + * StringUtils.center("a", 4, 'y') = "yayy" + * </pre> + * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @param padChar the character to pad the new String with + * @return centered String, {@code null} if null String input + * @since 2.0 + */ + public static String center(String str, int size, char padChar) { + if (str == null || size <= 0) { + return str; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; + } + str = leftPad(str, strLen + pads / 2, padChar); + str = rightPad(str, size, padChar); + return str; + } + + /** + * <p>Centers a String in a larger String of size {@code size}. + * Uses a supplied String as the value to pad the String with.</p> + * + * <p>If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.</p> + * + * <pre> + * StringUtils.center(null, *, *) = null + * StringUtils.center("", 4, " ") = " " + * StringUtils.center("ab", -1, " ") = "ab" + * StringUtils.center("ab", 4, " ") = " ab" + * StringUtils.center("abcd", 2, " ") = "abcd" + * StringUtils.center("a", 4, " ") = " a " + * StringUtils.center("a", 4, "yz") = "yayz" + * StringUtils.center("abc", 7, null) = " abc " + * StringUtils.center("abc", 7, "") = " abc " + * </pre> + * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @param padStr the String to pad the new String with, must not be null or empty + * @return centered String, {@code null} if null String input + * @throws IllegalArgumentException if padStr is {@code null} or empty + */ + public static String center(String str, int size, String padStr) { + if (str == null || size <= 0) { + return str; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; + } + str = leftPad(str, strLen + pads / 2, padStr); + str = rightPad(str, size, padStr); + return str; + } + + // Case conversion + //----------------------------------------------------------------------- + /** + * <p>Converts a String to upper case as per {@link String#toUpperCase()}.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.upperCase(null) = null + * StringUtils.upperCase("") = "" + * StringUtils.upperCase("aBc") = "ABC" + * </pre> + * + * <p><strong>Note:</strong> As described in the documentation for {@link String#toUpperCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p> + * + * @param str the String to upper case, may be null + * @return the upper cased String, {@code null} if null String input + */ + public static String upperCase(String str) { + if (str == null) { + return null; + } + return str.toUpperCase(); + } + + /** + * <p>Converts a String to upper case as per {@link String#toUpperCase(Locale)}.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.upperCase(null, Locale.ENGLISH) = null + * StringUtils.upperCase("", Locale.ENGLISH) = "" + * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC" + * </pre> + * + * @param str the String to upper case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the upper cased String, {@code null} if null String input + * @since 2.5 + */ + public static String upperCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toUpperCase(locale); + } + + /** + * <p>Converts a String to lower case as per {@link String#toLowerCase()}.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.lowerCase(null) = null + * StringUtils.lowerCase("") = "" + * StringUtils.lowerCase("aBc") = "abc" + * </pre> + * + * <p><strong>Note:</strong> As described in the documentation for {@link String#toLowerCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).</p> + * + * @param str the String to lower case, may be null + * @return the lower cased String, {@code null} if null String input + */ + public static String lowerCase(String str) { + if (str == null) { + return null; + } + return str.toLowerCase(); + } + + /** + * <p>Converts a String to lower case as per {@link String#toLowerCase(Locale)}.</p> + * + * <p>A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.lowerCase(null, Locale.ENGLISH) = null + * StringUtils.lowerCase("", Locale.ENGLISH) = "" + * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc" + * </pre> + * + * @param str the String to lower case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the lower cased String, {@code null} if null String input + * @since 2.5 + */ + public static String lowerCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toLowerCase(locale); + } + + /** + * <p>Capitalizes a String changing the first letter to title case as + * per {@link Character#toTitleCase(char)}. No other letters are changed.</p> + * + * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}. + * A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.capitalize(null) = null + * StringUtils.capitalize("") = "" + * StringUtils.capitalize("cat") = "Cat" + * StringUtils.capitalize("cAt") = "CAt" + * </pre> + * + * @param str the String to capitalize, may be null + * @return the capitalized String, {@code null} if null String input + * @see org.apache.commons.lang3.text.WordUtils#capitalize(String) + * @see #uncapitalize(String) + * @since 2.0 + */ + public static String capitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuilder(strLen) + .append(Character.toTitleCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + * <p>Uncapitalizes a String changing the first letter to title case as + * per {@link Character#toLowerCase(char)}. No other letters are changed.</p> + * + * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}. + * A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.uncapitalize(null) = null + * StringUtils.uncapitalize("") = "" + * StringUtils.uncapitalize("Cat") = "cat" + * StringUtils.uncapitalize("CAT") = "cAT" + * </pre> + * + * @param str the String to uncapitalize, may be null + * @return the uncapitalized String, {@code null} if null String input + * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String) + * @see #capitalize(String) + * @since 2.0 + */ + public static String uncapitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuilder(strLen) + .append(Character.toLowerCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + * <p>Swaps the case of a String changing upper and title case to + * lower case, and lower case to upper case.</p> + * + * <ul> + * <li>Upper case character converts to Lower case</li> + * <li>Title case character converts to Lower case</li> + * <li>Lower case character converts to Upper case</li> + * </ul> + * + * <p>For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}. + * A {@code null} input String returns {@code null}.</p> + * + * <pre> + * StringUtils.swapCase(null) = null + * StringUtils.swapCase("") = "" + * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone" + * </pre> + * + * <p>NOTE: This method changed in Lang version 2.0. + * It no longer performs a word based algorithm. + * If you only use ASCII, you will notice no change. + * That functionality is available in org.apache.commons.lang3.text.WordUtils.</p> + * + * @param str the String to swap case, may be null + * @return the changed String, {@code null} if null String input + */ + public static String swapCase(String str) { + if (StringUtils.isEmpty(str)) { + return str; + } + + char[] buffer = str.toCharArray(); + + for (int i = 0; i < buffer.length; i++) { + char ch = buffer[i]; + if (Character.isUpperCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + } else if (Character.isTitleCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + } else if (Character.isLowerCase(ch)) { + buffer[i] = Character.toUpperCase(ch); + } + } + return new String(buffer); + } + + // Count matches + //----------------------------------------------------------------------- + /** + * <p>Counts how many times the substring appears in the larger string.</p> + * + * <p>A {@code null} or empty ("") String input returns {@code 0}.</p> + * + * <pre> + * StringUtils.countMatches(null, *) = 0 + * StringUtils.countMatches("", *) = 0 + * StringUtils.countMatches("abba", null) = 0 + * StringUtils.countMatches("abba", "") = 0 + * StringUtils.countMatches("abba", "a") = 2 + * StringUtils.countMatches("abba", "ab") = 1 + * StringUtils.countMatches("abba", "xxx") = 0 + * </pre> + * + * @param str the CharSequence to check, may be null + * @param sub the substring to count, may be null + * @return the number of occurrences, 0 if either CharSequence is {@code null} + * @since 3.0 Changed signature from countMatches(String, String) to countMatches(CharSequence, CharSequence) + */ + public static int countMatches(CharSequence str, CharSequence sub) { + if (isEmpty(str) || isEmpty(sub)) { + return 0; + } + int count = 0; + int idx = 0; + while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //----------------------------------------------------------------------- + /** + * <p>Checks if the CharSequence contains only Unicode letters.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.</p> + * + * <pre> + * StringUtils.isAlpha(null) = false + * StringUtils.isAlpha("") = false + * StringUtils.isAlpha(" ") = false + * StringUtils.isAlpha("abc") = true + * StringUtils.isAlpha("ab2c") = false + * StringUtils.isAlpha("ab-c") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters, and is non-null + * @since 3.0 Changed signature from isAlpha(String) to isAlpha(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isAlpha(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetter(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only Unicode letters and + * space (' ').</p> + * + * <p>{@code null} will return {@code false} + * An empty CharSequence (length()=0) will return {@code true}.</p> + * + * <pre> + * StringUtils.isAlphaSpace(null) = false + * StringUtils.isAlphaSpace("") = true + * StringUtils.isAlphaSpace(" ") = true + * StringUtils.isAlphaSpace("abc") = true + * StringUtils.isAlphaSpace("ab c") = true + * StringUtils.isAlphaSpace("ab2c") = false + * StringUtils.isAlphaSpace("ab-c") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters and space, + * and is non-null + * @since 3.0 Changed signature from isAlphaSpace(String) to isAlphaSpace(CharSequence) + */ + public static boolean isAlphaSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if ((Character.isLetter(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only Unicode letters or digits.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.</p> + * + * <pre> + * StringUtils.isAlphanumeric(null) = false + * StringUtils.isAlphanumeric("") = false + * StringUtils.isAlphanumeric(" ") = false + * StringUtils.isAlphanumeric("abc") = true + * StringUtils.isAlphanumeric("ab c") = false + * StringUtils.isAlphanumeric("ab2c") = true + * StringUtils.isAlphanumeric("ab-c") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters or digits, + * and is non-null + * @since 3.0 Changed signature from isAlphanumeric(String) to isAlphanumeric(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isAlphanumeric(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetterOrDigit(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only Unicode letters, digits + * or space ({@code ' '}).</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.</p> + * + * <pre> + * StringUtils.isAlphanumericSpace(null) = false + * StringUtils.isAlphanumericSpace("") = true + * StringUtils.isAlphanumericSpace(" ") = true + * StringUtils.isAlphanumericSpace("abc") = true + * StringUtils.isAlphanumericSpace("ab c") = true + * StringUtils.isAlphanumericSpace("ab2c") = true + * StringUtils.isAlphanumericSpace("ab-c") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters, digits or space, + * and is non-null + * @since 3.0 Changed signature from isAlphanumericSpace(String) to isAlphanumericSpace(CharSequence) + */ + public static boolean isAlphanumericSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if ((Character.isLetterOrDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only ASCII printable characters.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.</p> + * + * <pre> + * StringUtils.isAsciiPrintable(null) = false + * StringUtils.isAsciiPrintable("") = true + * StringUtils.isAsciiPrintable(" ") = true + * StringUtils.isAsciiPrintable("Ceki") = true + * StringUtils.isAsciiPrintable("ab2c") = true + * StringUtils.isAsciiPrintable("!ab-c~") = true + * StringUtils.isAsciiPrintable("\u0020") = true + * StringUtils.isAsciiPrintable("\u0021") = true + * StringUtils.isAsciiPrintable("\u007e") = true + * StringUtils.isAsciiPrintable("\u007f") = false + * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if every character is in the range + * 32 thru 126 + * @since 2.1 + * @since 3.0 Changed signature from isAsciiPrintable(String) to isAsciiPrintable(CharSequence) + */ + public static boolean isAsciiPrintable(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (CharUtils.isAsciiPrintable(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only Unicode digits. + * A decimal point is not a Unicode digit and returns false.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.</p> + * + * <pre> + * StringUtils.isNumeric(null) = false + * StringUtils.isNumeric("") = false + * StringUtils.isNumeric(" ") = false + * StringUtils.isNumeric("123") = true + * StringUtils.isNumeric("12 3") = false + * StringUtils.isNumeric("ab2c") = false + * StringUtils.isNumeric("12-3") = false + * StringUtils.isNumeric("12.3") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains digits, and is non-null + * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isNumeric(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isDigit(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only Unicode digits or space + * ({@code ' '}). + * A decimal point is not a Unicode digit and returns false.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.</p> + * + * <pre> + * StringUtils.isNumericSpace(null) = false + * StringUtils.isNumericSpace("") = true + * StringUtils.isNumericSpace(" ") = true + * StringUtils.isNumericSpace("123") = true + * StringUtils.isNumericSpace("12 3") = true + * StringUtils.isNumericSpace("ab2c") = false + * StringUtils.isNumericSpace("12-3") = false + * StringUtils.isNumericSpace("12.3") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains digits or space, + * and is non-null + * @since 3.0 Changed signature from isNumericSpace(String) to isNumericSpace(CharSequence) + */ + public static boolean isNumericSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if ((Character.isDigit(cs.charAt(i)) == false) && (cs.charAt(i) != ' ')) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only whitespace.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.</p> + * + * <pre> + * StringUtils.isWhitespace(null) = false + * StringUtils.isWhitespace("") = true + * StringUtils.isWhitespace(" ") = true + * StringUtils.isWhitespace("abc") = false + * StringUtils.isWhitespace("ab2c") = false + * StringUtils.isWhitespace("ab-c") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains whitespace, and is non-null + * @since 2.0 + * @since 3.0 Changed signature from isWhitespace(String) to isWhitespace(CharSequence) + */ + public static boolean isWhitespace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if ((Character.isWhitespace(cs.charAt(i)) == false)) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only lowercase characters.</p> + * + * <p>{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.</p> + * + * <pre> + * StringUtils.isAllLowerCase(null) = false + * StringUtils.isAllLowerCase("") = false + * StringUtils.isAllLowerCase(" ") = false + * StringUtils.isAllLowerCase("abc") = true + * StringUtils.isAllLowerCase("abC") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains lowercase characters, and is non-null + * @since 2.5 + * @since 3.0 Changed signature from isAllLowerCase(String) to isAllLowerCase(CharSequence) + */ + public static boolean isAllLowerCase(CharSequence cs) { + if (cs == null || isEmpty(cs)) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLowerCase(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + * <p>Checks if the CharSequence contains only uppercase characters.</p> + * + * <p>{@code null} will return {@code false}. + * An empty String (length()=0) will return {@code false}.</p> + * + * <pre> + * StringUtils.isAllUpperCase(null) = false + * StringUtils.isAllUpperCase("") = false + * StringUtils.isAllUpperCase(" ") = false + * StringUtils.isAllUpperCase("ABC") = true + * StringUtils.isAllUpperCase("aBC") = false + * </pre> + * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains uppercase characters, and is non-null + * @since 2.5 + * @since 3.0 Changed signature from isAllUpperCase(String) to isAllUpperCase(CharSequence) + */ + public static boolean isAllUpperCase(CharSequence cs) { + if (cs == null || isEmpty(cs)) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isUpperCase(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + // Defaults + //----------------------------------------------------------------------- + /** + * <p>Returns either the passed in String, + * or if the String is {@code null}, an empty String ("").</p> + * + * <pre> + * StringUtils.defaultString(null) = "" + * StringUtils.defaultString("") = "" + * StringUtils.defaultString("bat") = "bat" + * </pre> + * + * @see ObjectUtils#toString(Object) + * @see String#valueOf(Object) + * @param str the String to check, may be null + * @return the passed in String, or the empty String if it + * was {@code null} + */ + public static String defaultString(String str) { + return str == null ? EMPTY : str; + } + + /** + * <p>Returns either the passed in String, or if the String is + * {@code null}, the value of {@code defaultStr}.</p> + * + * <pre> + * StringUtils.defaultString(null, "NULL") = "NULL" + * StringUtils.defaultString("", "NULL") = "" + * StringUtils.defaultString("bat", "NULL") = "bat" + * </pre> + * + * @see ObjectUtils#toString(Object,String) + * @see String#valueOf(Object) + * @param str the String to check, may be null + * @param defaultStr the default String to return + * if the input is {@code null}, may be null + * @return the passed in String, or the default if it was {@code null} + */ + public static String defaultString(String str, String defaultStr) { + return str == null ? defaultStr : str; + } + + /** + * <p>Returns either the passed in CharSequence, or if the CharSequence is + * whitespace, empty ("") or {@code null}, the value of {@code defaultStr}.</p> + * + * <pre> + * StringUtils.defaultIfBlank(null, "NULL") = "NULL" + * StringUtils.defaultIfBlank("", "NULL") = "NULL" + * StringUtils.defaultIfBlank(" ", "NULL") = "NULL" + * StringUtils.defaultIfBlank("bat", "NULL") = "bat" + * StringUtils.defaultIfBlank("", null) = null + * </pre> + * @param <T> the specific kind of CharSequence + * @param str the CharSequence to check, may be null + * @param defaultStr the default CharSequence to return + * if the input is whitespace, empty ("") or {@code null}, may be null + * @return the passed in CharSequence, or the default + * @see StringUtils#defaultString(String, String) + */ + public static <T extends CharSequence> T defaultIfBlank(T str, T defaultStr) { + return StringUtils.isBlank(str) ? defaultStr : str; + } + + /** + * <p>Returns either the passed in CharSequence, or if the CharSequence is + * empty or {@code null}, the value of {@code defaultStr}.</p> + * + * <pre> + * StringUtils.defaultIfEmpty(null, "NULL") = "NULL" + * StringUtils.defaultIfEmpty("", "NULL") = "NULL" + * StringUtils.defaultIfEmpty("bat", "NULL") = "bat" + * StringUtils.defaultIfEmpty("", null) = null + * </pre> + * @param <T> the specific kind of CharSequence + * @param str the CharSequence to check, may be null + * @param defaultStr the default CharSequence to return + * if the input is empty ("") or {@code null}, may be null + * @return the passed in CharSequence, or the default + * @see StringUtils#defaultString(String, String) + */ + public static <T extends CharSequence> T defaultIfEmpty(T str, T defaultStr) { + return StringUtils.isEmpty(str) ? defaultStr : str; + } + + // Reversing + //----------------------------------------------------------------------- + /** + * <p>Reverses a String as per {@link StringBuilder#reverse()}.</p> + * + * <p>A {@code null} String returns {@code null}.</p> + * + * <pre> + * StringUtils.reverse(null) = null + * StringUtils.reverse("") = "" + * StringUtils.reverse("bat") = "tab" + * </pre> + * + * @param str the String to reverse, may be null + * @return the reversed String, {@code null} if null String input + */ + public static String reverse(String str) { + if (str == null) { + return null; + } + return new StringBuilder(str).reverse().toString(); + } + + /** + * <p>Reverses a String that is delimited by a specific character.</p> + * + * <p>The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is {@code '.'}).</p> + * + * <pre> + * StringUtils.reverseDelimited(null, *) = null + * StringUtils.reverseDelimited("", *) = "" + * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c" + * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a" + * </pre> + * + * @param str the String to reverse, may be null + * @param separatorChar the separator character to use + * @return the reversed String, {@code null} if null String input + * @since 2.0 + */ + public static String reverseDelimited(String str, char separatorChar) { + if (str == null) { + return null; + } + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split(str, separatorChar); + ArrayUtils.reverse(strs); + return join(strs, separatorChar); + } + + // Abbreviating + //----------------------------------------------------------------------- + /** + * <p>Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "Now is the time for..."</p> + * + * <p>Specifically: + * <ul> + * <li>If {@code str} is less than {@code maxWidth} characters + * long, return it.</li> + * <li>Else abbreviate it to {@code (substring(str, 0, max-3) + "...")}.</li> + * <li>If {@code maxWidth} is less than {@code 4}, throw an + * {@code IllegalArgumentException}.</li> + * <li>In no case will it return a String of length greater than + * {@code maxWidth}.</li> + * </ul> + * </p> + * + * <pre> + * StringUtils.abbreviate(null, *) = null + * StringUtils.abbreviate("", 4) = "" + * StringUtils.abbreviate("abcdefg", 6) = "abc..." + * StringUtils.abbreviate("abcdefg", 7) = "abcdefg" + * StringUtils.abbreviate("abcdefg", 8) = "abcdefg" + * StringUtils.abbreviate("abcdefg", 4) = "a..." + * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException + * </pre> + * + * @param str the String to check, may be null + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, {@code null} if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int maxWidth) { + return abbreviate(str, 0, maxWidth); + } + + /** + * <p>Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "...is the time for..."</p> + * + * <p>Works like {@code abbreviate(String, int)}, but allows you to specify + * a "left edge" offset. Note that this left edge is not necessarily going to + * be the leftmost character in the result, or the first character following the + * ellipses, but it will appear somewhere in the result. + * + * <p>In no case will it return a String of length greater than + * {@code maxWidth}.</p> + * + * <pre> + * StringUtils.abbreviate(null, *, *) = null + * StringUtils.abbreviate("", 0, 4) = "" + * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..." + * StringUtils.abbreviate("abcdefghijklmno", 0, 10) = "abcdefg..." + * StringUtils.abbreviate("abcdefghijklmno", 1, 10) = "abcdefg..." + * StringUtils.abbreviate("abcdefghijklmno", 4, 10) = "abcdefg..." + * StringUtils.abbreviate("abcdefghijklmno", 5, 10) = "...fghi..." + * StringUtils.abbreviate("abcdefghijklmno", 6, 10) = "...ghij..." + * StringUtils.abbreviate("abcdefghijklmno", 8, 10) = "...ijklmno" + * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno" + * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno" + * StringUtils.abbreviate("abcdefghij", 0, 3) = IllegalArgumentException + * StringUtils.abbreviate("abcdefghij", 5, 6) = IllegalArgumentException + * </pre> + * + * @param str the String to check, may be null + * @param offset left edge of source String + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, {@code null} if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int offset, int maxWidth) { + if (str == null) { + return null; + } + if (maxWidth < 4) { + throw new IllegalArgumentException("Minimum abbreviation width is 4"); + } + if (str.length() <= maxWidth) { + return str; + } + if (offset > str.length()) { + offset = str.length(); + } + if ((str.length() - offset) < (maxWidth - 3)) { + offset = str.length() - (maxWidth - 3); + } + final String abrevMarker = "..."; + if (offset <= 4) { + return str.substring(0, maxWidth - 3) + abrevMarker; + } + if (maxWidth < 7) { + throw new IllegalArgumentException("Minimum abbreviation width with offset is 7"); + } + if ((offset + (maxWidth - 3)) < str.length()) { + return abrevMarker + abbreviate(str.substring(offset), maxWidth - 3); + } + return abrevMarker + str.substring(str.length() - (maxWidth - 3)); + } + + /** + * <p>Abbreviates a String to the length passed, replacing the middle characters with the supplied + * replacement String.</p> + * + * <p>This abbreviation only occurs if the following criteria is met: + * <ul> + * <li>Neither the String for abbreviation nor the replacement String are null or empty </li> + * <li>The length to truncate to is less than the length of the supplied String</li> + * <li>The length to truncate to is greater than 0</li> + * <li>The abbreviated String will have enough room for the length supplied replacement String + * and the first and last characters of the supplied String for abbreviation</li> + * </ul> + * Otherwise, the returned String will be the same as the supplied String for abbreviation. + * </p> + * + * <pre> + * StringUtils.abbreviateMiddle(null, null, 0) = null + * StringUtils.abbreviateMiddle("abc", null, 0) = "abc" + * StringUtils.abbreviateMiddle("abc", ".", 0) = "abc" + * StringUtils.abbreviateMiddle("abc", ".", 3) = "abc" + * StringUtils.abbreviateMiddle("abcdef", ".", 4) = "ab.f" + * </pre> + * + * @param str the String to abbreviate, may be null + * @param middle the String to replace the middle characters with, may be null + * @param length the length to abbreviate {@code str} to. + * @return the abbreviated String if the above criteria is met, or the original String supplied for abbreviation. + * @since 2.5 + */ + public static String abbreviateMiddle(String str, String middle, int length) { + if (isEmpty(str) || isEmpty(middle)) { + return str; + } + + if (length >= str.length() || length < (middle.length()+2)) { + return str; + } + + int targetSting = length-middle.length(); + int startOffset = targetSting/2+targetSting%2; + int endOffset = str.length()-targetSting/2; + + StringBuilder builder = new StringBuilder(length); + builder.append(str.substring(0,startOffset)); + builder.append(middle); + builder.append(str.substring(endOffset)); + + return builder.toString(); + } + + // Difference + //----------------------------------------------------------------------- + /** + * <p>Compares two Strings, and returns the portion where they differ. + * (More precisely, return the remainder of the second String, + * starting from where it's different from the first.)</p> + * + * <p>For example, + * {@code difference("i am a machine", "i am a robot") -> "robot"}.</p> + * + * <pre> + * StringUtils.difference(null, null) = null + * StringUtils.difference("", "") = "" + * StringUtils.difference("", "abc") = "abc" + * StringUtils.difference("abc", "") = "" + * StringUtils.difference("abc", "abc") = "" + * StringUtils.difference("ab", "abxyz") = "xyz" + * StringUtils.difference("abcde", "abxyz") = "xyz" + * StringUtils.difference("abcde", "xyz") = "xyz" + * </pre> + * + * @param str1 the first String, may be null + * @param str2 the second String, may be null + * @return the portion of str2 where it differs from str1; returns the + * empty String if they are equal + * @since 2.0 + */ + public static String difference(String str1, String str2) { + if (str1 == null) { + return str2; + } + if (str2 == null) { + return str1; + } + int at = indexOfDifference(str1, str2); + if (at == INDEX_NOT_FOUND) { + return EMPTY; + } + return str2.substring(at); + } + + /** + * <p>Compares two CharSequences, and returns the index at which the + * CharSequences begin to differ.</p> + * + * <p>For example, + * {@code indexOfDifference("i am a machine", "i am a robot") -> 7}</p> + * + * <pre> + * StringUtils.indexOfDifference(null, null) = -1 + * StringUtils.indexOfDifference("", "") = -1 + * StringUtils.indexOfDifference("", "abc") = 0 + * StringUtils.indexOfDifference("abc", "") = 0 + * StringUtils.indexOfDifference("abc", "abc") = -1 + * StringUtils.indexOfDifference("ab", "abxyz") = 2 + * StringUtils.indexOfDifference("abcde", "abxyz") = 2 + * StringUtils.indexOfDifference("abcde", "xyz") = 0 + * </pre> + * + * @param cs1 the first CharSequence, may be null + * @param cs2 the second CharSequence, may be null + * @return the index where cs1 and cs2 begin to differ; -1 if they are equal + * @since 2.0 + * @since 3.0 Changed signature from indexOfDifference(String, String) to + * indexOfDifference(CharSequence, CharSequence) + */ + public static int indexOfDifference(CharSequence cs1, CharSequence cs2) { + if (cs1 == cs2) { + return INDEX_NOT_FOUND; + } + if (cs1 == null || cs2 == null) { + return 0; + } + int i; + for (i = 0; i < cs1.length() && i < cs2.length(); ++i) { + if (cs1.charAt(i) != cs2.charAt(i)) { + break; + } + } + if (i < cs2.length() || i < cs1.length()) { + return i; + } + return INDEX_NOT_FOUND; + } + + /** + * <p>Compares all CharSequences in an array and returns the index at which the + * CharSequences begin to differ.</p> + * + * <p>For example, + * <code>indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7</code></p> + * + * <pre> + * StringUtils.indexOfDifference(null) = -1 + * StringUtils.indexOfDifference(new String[] {}) = -1 + * StringUtils.indexOfDifference(new String[] {"abc"}) = -1 + * StringUtils.indexOfDifference(new String[] {null, null}) = -1 + * StringUtils.indexOfDifference(new String[] {"", ""}) = -1 + * StringUtils.indexOfDifference(new String[] {"", null}) = 0 + * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0 + * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0 + * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0 + * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0 + * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1 + * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1 + * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2 + * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2 + * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0 + * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0 + * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7 + * </pre> + * + * @param css array of CharSequences, entries may be null + * @return the index where the strings begin to differ; -1 if they are all equal + * @since 2.4 + * @since 3.0 Changed signature from indexOfDifference(String...) to indexOfDifference(CharSequence...) + */ + public static int indexOfDifference(CharSequence... css) { + if (css == null || css.length <= 1) { + return INDEX_NOT_FOUND; + } + boolean anyStringNull = false; + boolean allStringsNull = true; + int arrayLen = css.length; + int shortestStrLen = Integer.MAX_VALUE; + int longestStrLen = 0; + + // find the min and max string lengths; this avoids checking to make + // sure we are not exceeding the length of the string each time through + // the bottom loop. + for (int i = 0; i < arrayLen; i++) { + if (css[i] == null) { + anyStringNull = true; + shortestStrLen = 0; + } else { + allStringsNull = false; + shortestStrLen = Math.min(css[i].length(), shortestStrLen); + longestStrLen = Math.max(css[i].length(), longestStrLen); + } + } + + // handle lists containing all nulls or all empty strings + if (allStringsNull || (longestStrLen == 0 && !anyStringNull)) { + return INDEX_NOT_FOUND; + } + + // handle lists containing some nulls or some empty strings + if (shortestStrLen == 0) { + return 0; + } + + // find the position with the first difference across all strings + int firstDiff = -1; + for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) { + char comparisonChar = css[0].charAt(stringPos); + for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) { + if (css[arrayPos].charAt(stringPos) != comparisonChar) { + firstDiff = stringPos; + break; + } + } + if (firstDiff != -1) { + break; + } + } + + if (firstDiff == -1 && shortestStrLen != longestStrLen) { + // we compared all of the characters up to the length of the + // shortest string and didn't find a match, but the string lengths + // vary, so return the length of the shortest string. + return shortestStrLen; + } + return firstDiff; + } + + /** + * <p>Compares all Strings in an array and returns the initial sequence of + * characters that is common to all of them.</p> + * + * <p>For example, + * <code>getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "</code></p> + * + * <pre> + * StringUtils.getCommonPrefix(null) = "" + * StringUtils.getCommonPrefix(new String[] {}) = "" + * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc" + * StringUtils.getCommonPrefix(new String[] {null, null}) = "" + * StringUtils.getCommonPrefix(new String[] {"", ""}) = "" + * StringUtils.getCommonPrefix(new String[] {"", null}) = "" + * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = "" + * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = "" + * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = "" + * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = "" + * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc" + * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a" + * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab" + * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab" + * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = "" + * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = "" + * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a " + * </pre> + * + * @param strs array of String objects, entries may be null + * @return the initial sequence of characters that are common to all Strings + * in the array; empty String if the array is null, the elements are all null + * or if there is no common prefix. + * @since 2.4 + */ + public static String getCommonPrefix(String... strs) { + if (strs == null || strs.length == 0) { + return EMPTY; + } + int smallestIndexOfDiff = indexOfDifference(strs); + if (smallestIndexOfDiff == INDEX_NOT_FOUND) { + // all strings were identical + if (strs[0] == null) { + return EMPTY; + } + return strs[0]; + } else if (smallestIndexOfDiff == 0) { + // there were no common initial characters + return EMPTY; + } else { + // we found a common initial character sequence + return strs[0].substring(0, smallestIndexOfDiff); + } + } + + // Misc + //----------------------------------------------------------------------- + /** + * <p>Find the Levenshtein distance between two Strings.</p> + * + * <p>This is the number of changes needed to change one String into + * another, where each change is a single character modification (deletion, + * insertion or substitution).</p> + * + * <p>The previous implementation of the Levenshtein distance algorithm + * was from <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p> + * + * <p>Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError + * which can occur when my Java implementation is used with very large strings.<br> + * This implementation of the Levenshtein distance algorithm + * is from <a href="http://www.merriampark.com/ldjava.htm">http://www.merriampark.com/ldjava.htm</a></p> + * + * <pre> + * StringUtils.getLevenshteinDistance(null, *) = IllegalArgumentException + * StringUtils.getLevenshteinDistance(*, null) = IllegalArgumentException + * StringUtils.getLevenshteinDistance("","") = 0 + * StringUtils.getLevenshteinDistance("","a") = 1 + * StringUtils.getLevenshteinDistance("aaapppp", "") = 7 + * StringUtils.getLevenshteinDistance("frog", "fog") = 1 + * StringUtils.getLevenshteinDistance("fly", "ant") = 3 + * StringUtils.getLevenshteinDistance("elephant", "hippo") = 7 + * StringUtils.getLevenshteinDistance("hippo", "elephant") = 7 + * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8 + * StringUtils.getLevenshteinDistance("hello", "hallo") = 1 + * </pre> + * + * @param s the first String, must not be null + * @param t the second String, must not be null + * @return result distance + * @throws IllegalArgumentException if either String input {@code null} + * @since 3.0 Changed signature from getLevenshteinDistance(String, String) to + * getLevenshteinDistance(CharSequence, CharSequence) + */ + public static int getLevenshteinDistance(CharSequence s, CharSequence t) { + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + + /* + The difference between this impl. and the previous is that, rather + than creating and retaining a matrix of size s.length() + 1 by t.length() + 1, + we maintain two single-dimensional arrays of length s.length() + 1. The first, d, + is the 'current working' distance array that maintains the newest distance cost + counts as we iterate through the characters of String s. Each time we increment + the index of String t we are comparing, d is copied to p, the second int[]. Doing so + allows us to retain the previous cost counts as required by the algorithm (taking + the minimum of the cost count to the left, up one, and diagonally up and to the left + of the current cost count being calculated). (Note that the arrays aren't really + copied anymore, just switched...this is clearly much better than cloning an array + or doing a System.arraycopy() each time through the outer loop.) + + Effectively, the difference between the two implementations is this one does not + cause an out of memory condition when calculating the LD over two very large strings. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + + if (n == 0) { + return m; + } else if (m == 0) { + return n; + } + + if (n > m) { + // swap the input strings to consume less memory + CharSequence tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + int p[] = new int[n + 1]; //'previous' cost array, horizontally + int d[] = new int[n + 1]; // cost array, horizontally + int _d[]; //placeholder to assist in swapping p and d + + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + + char t_j; // jth character of t + + int cost; // cost + + for (i = 0; i <= n; i++) { + p[i] = i; + } + + for (j = 1; j <= m; j++) { + t_j = t.charAt(j - 1); + d[0] = j; + + for (i = 1; i <= n; i++) { + cost = s.charAt(i - 1) == t_j ? 0 : 1; + // minimum of cell to the left+1, to the top+1, diagonally left and up +cost + d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost); + } + + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } + + // our last action in the above loop was to switch d and p, so p now + // actually has the most recent cost counts + return p[n]; + } + + /** + * <p>Find the Levenshtein distance between two Strings if it's less than or equal to a given + * threshold.</p> + * + * <p>This is the number of changes needed to change one String into + * another, where each change is a single character modification (deletion, + * insertion or substitution).</p> + * + * <p>This implementation follows from Algorithms on Strings, Trees and Sequences by Dan Gusfield + * and Chas Emerick's implementation of the Levenshtein distance algorithm from + * <a href="http://www.merriampark.com/ld.htm">http://www.merriampark.com/ld.htm</a></p> + * + * <pre> + * StringUtils.getLevenshteinDistance(null, *, *) = IllegalArgumentException + * StringUtils.getLevenshteinDistance(*, null, *) = IllegalArgumentException + * StringUtils.getLevenshteinDistance(*, *, -1) = IllegalArgumentException + * StringUtils.getLevenshteinDistance("","", 0) = 0 + * StringUtils.getLevenshteinDistance("aaapppp", "", 8) = 7 + * StringUtils.getLevenshteinDistance("aaapppp", "", 7) = 7 + * StringUtils.getLevenshteinDistance("aaapppp", "", 6)) = -1 + * StringUtils.getLevenshteinDistance("elephant", "hippo", 7) = 7 + * StringUtils.getLevenshteinDistance("elephant", "hippo", 6) = -1 + * StringUtils.getLevenshteinDistance("hippo", "elephant", 7) = 7 + * StringUtils.getLevenshteinDistance("hippo", "elephant", 6) = -1 + * </pre> + * + * @param s the first String, must not be null + * @param t the second String, must not be null + * @param threshold the target threshold, must not be negative + * @return result distance, or {@code -1} if the distance would be greater than the threshold + * @throws IllegalArgumentException if either String input {@code null} or negative threshold + */ + public static int getLevenshteinDistance(CharSequence s, CharSequence t, int threshold) { + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + if (threshold < 0) { + throw new IllegalArgumentException("Threshold must not be negative"); + } + + /* + This implementation only computes the distance if it's less than or equal to the + threshold value, returning -1 if it's greater. The advantage is performance: unbounded + distance is O(nm), but a bound of k allows us to reduce it to O(km) time by only + computing a diagonal stripe of width 2k + 1 of the cost table. + It is also possible to use this to compute the unbounded Levenshtein distance by starting + the threshold at 1 and doubling each time until the distance is found; this is O(dm), where + d is the distance. + + One subtlety comes from needing to ignore entries on the border of our stripe + eg. + p[] = |#|#|#|* + d[] = *|#|#|#| + We must ignore the entry to the left of the leftmost member + We must ignore the entry above the rightmost member + + Another subtlety comes from our stripe running off the matrix if the strings aren't + of the same size. Since string s is always swapped to be the shorter of the two, + the stripe will always run off to the upper right instead of the lower left of the matrix. + + As a concrete example, suppose s is of length 5, t is of length 7, and our threshold is 1. + In this case we're going to walk a stripe of length 3. The matrix would look like so: + + 1 2 3 4 5 + 1 |#|#| | | | + 2 |#|#|#| | | + 3 | |#|#|#| | + 4 | | |#|#|#| + 5 | | | |#|#| + 6 | | | | |#| + 7 | | | | | | + + Note how the stripe leads off the table as there is no possible way to turn a string of length 5 + into one of length 7 in edit distance of 1. + + Additionally, this implementation decreases memory usage by using two + single-dimensional arrays and swapping them back and forth instead of allocating + an entire n by m matrix. This requires a few minor changes, such as immediately returning + when it's detected that the stripe has run off the matrix and initially filling the arrays with + large values so that entries we don't compute are ignored. + + See Algorithms on Strings, Trees and Sequences by Dan Gusfield for some discussion. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + + // if one string is empty, the edit distance is necessarily the length of the other + if (n == 0) { + return m <= threshold ? m : -1; + } else if (m == 0) { + return n <= threshold ? n : -1; + } + + if (n > m) { + // swap the two strings to consume less memory + CharSequence tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + int p[] = new int[n + 1]; // 'previous' cost array, horizontally + int d[] = new int[n + 1]; // cost array, horizontally + int _d[]; // placeholder to assist in swapping p and d + + // fill in starting table values + int boundary = Math.min(n, threshold) + 1; + for (int i = 0; i < boundary; i++) { + p[i] = i; + } + // these fills ensure that the value above the rightmost entry of our + // stripe will be ignored in following loop iterations + Arrays.fill(p, boundary, p.length, Integer.MAX_VALUE); + Arrays.fill(d, Integer.MAX_VALUE); + + // iterates through t + for (int j = 1; j <= m; j++) { + char t_j = t.charAt(j - 1); // jth character of t + d[0] = j; + + // compute stripe indices, constrain to array size + int min = Math.max(1, j - threshold); + int max = Math.min(n, j + threshold); + + // the stripe may lead off of the table if s and t are of different sizes + if (min > max) { + return -1; + } + + // ignore entry left of leftmost + if (min > 1) { + d[min - 1] = Integer.MAX_VALUE; + } + + // iterates through [min, max] in s + for (int i = min; i <= max; i++) { + if (s.charAt(i - 1) == t_j) { + // diagonally left and up + d[i] = p[i - 1]; + } else { + // 1 + minimum of cell to the left, to the top, diagonally left and up + d[i] = 1 + Math.min(Math.min(d[i - 1], p[i]), p[i - 1]); + } + } + + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } + + // if p[n] is greater than the threshold, there's no guarantee on it being the correct + // distance + if (p[n] <= threshold) { + return p[n]; + } else { + return -1; + } + } + + // startsWith + //----------------------------------------------------------------------- + + /** + * <p>Check if a CharSequence starts with a specified prefix.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.</p> + * + * <pre> + * StringUtils.startsWith(null, null) = true + * StringUtils.startsWith(null, "abc") = false + * StringUtils.startsWith("abcdef", null) = false + * StringUtils.startsWith("abcdef", "abc") = true + * StringUtils.startsWith("ABCDEF", "abc") = false + * </pre> + * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence) + */ + public static boolean startsWith(CharSequence str, CharSequence prefix) { + return startsWith(str, prefix, false); + } + + /** + * <p>Case insensitive check if a CharSequence starts with a specified prefix.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case insensitive.</p> + * + * <pre> + * StringUtils.startsWithIgnoreCase(null, null) = true + * StringUtils.startsWithIgnoreCase(null, "abc") = false + * StringUtils.startsWithIgnoreCase("abcdef", null) = false + * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true + * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true + * </pre> + * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence) + */ + public static boolean startsWithIgnoreCase(CharSequence str, CharSequence prefix) { + return startsWith(str, prefix, true); + } + + /** + * <p>Check if a CharSequence starts with a specified prefix (optionally case insensitive).</p> + * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @param ignoreCase indicates whether the compare should ignore case + * (case insensitive) or not. + * @return {@code true} if the CharSequence starts with the prefix or + * both {@code null} + */ + private static boolean startsWith(CharSequence str, CharSequence prefix, boolean ignoreCase) { + if (str == null || prefix == null) { + return (str == null && prefix == null); + } + if (prefix.length() > str.length()) { + return false; + } + return CharSequenceUtils.regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length()); + } + + /** + * <p>Check if a CharSequence starts with any of an array of specified strings.</p> + * + * <pre> + * StringUtils.startsWithAny(null, null) = false + * StringUtils.startsWithAny(null, new String[] {"abc"}) = false + * StringUtils.startsWithAny("abcxyz", null) = false + * StringUtils.startsWithAny("abcxyz", new String[] {""}) = false + * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true + * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true + * </pre> + * + * @param string the CharSequence to check, may be null + * @param searchStrings the CharSequences to find, may be null or empty + * @return {@code true} if the CharSequence starts with any of the the prefixes, case insensitive, or + * both {@code null} + * @since 2.5 + * @since 3.0 Changed signature from startsWithAny(String, String[]) to startsWithAny(CharSequence, CharSequence...) + */ + public static boolean startsWithAny(CharSequence string, CharSequence... searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (CharSequence searchString : searchStrings) { + if (StringUtils.startsWith(string, searchString)) { + return true; + } + } + return false; + } + + // endsWith + //----------------------------------------------------------------------- + + /** + * <p>Check if a CharSequence ends with a specified suffix.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.</p> + * + * <pre> + * StringUtils.endsWith(null, null) = true + * StringUtils.endsWith(null, "def") = false + * StringUtils.endsWith("abcdef", null) = false + * StringUtils.endsWith("abcdef", "def") = true + * StringUtils.endsWith("ABCDEF", "def") = false + * StringUtils.endsWith("ABCDEF", "cde") = false + * </pre> + * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @return {@code true} if the CharSequence ends with the suffix, case sensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from endsWith(String, String) to endsWith(CharSequence, CharSequence) + */ + public static boolean endsWith(CharSequence str, CharSequence suffix) { + return endsWith(str, suffix, false); + } + + /** + * <p>Case insensitive check if a CharSequence ends with a specified suffix.</p> + * + * <p>{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case insensitive.</p> + * + * <pre> + * StringUtils.endsWithIgnoreCase(null, null) = true + * StringUtils.endsWithIgnoreCase(null, "def") = false + * StringUtils.endsWithIgnoreCase("abcdef", null) = false + * StringUtils.endsWithIgnoreCase("abcdef", "def") = true + * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true + * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false + * </pre> + * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @return {@code true} if the CharSequence ends with the suffix, case insensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from endsWithIgnoreCase(String, String) to endsWithIgnoreCase(CharSequence, CharSequence) + */ + public static boolean endsWithIgnoreCase(CharSequence str, CharSequence suffix) { + return endsWith(str, suffix, true); + } + + /** + * <p>Check if a CharSequence ends with a specified suffix (optionally case insensitive).</p> + * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @param ignoreCase indicates whether the compare should ignore case + * (case insensitive) or not. + * @return {@code true} if the CharSequence starts with the prefix or + * both {@code null} + */ + private static boolean endsWith(CharSequence str, CharSequence suffix, boolean ignoreCase) { + if (str == null || suffix == null) { + return str == null && suffix == null; + } + if (suffix.length() > str.length()) { + return false; + } + int strOffset = str.length() - suffix.length(); + return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length()); + } + + /** + * <p> + * Similar to <a + * href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize + * -space</a> + * </p> + * <p> + * The function returns the argument string with whitespace normalized by using + * <code>{@link #trim(String)}</code> to remove leading and trailing whitespace + * and then replacing sequences of whitespace characters by a single space. + * </p> + * In XML Whitespace characters are the same as those allowed by the <a + * href="http://www.w3.org/TR/REC-xml/#NT-S">S</a> production, which is S ::= (#x20 | #x9 | #xD | #xA)+ + * <p> + * Java's regexp pattern \s defines whitespace as [ \t\n\x0B\f\r] + * <p> + * For reference: + * <ul> + * <li>\x0B = vertical tab</li> + * <li>\f = #xC = form feed</li> + * <li>#x20 = space</li> + * <li>#x9 = \t</li> + * <li>#xA = \n</li> + * <li>#xD = \r</li> + * </ul> + * </p> + * <p> + * The difference is that Java's whitespace includes vertical tab and form feed, which this functional will also + * normalize. Additionally <code>{@link #trim(String)}</code> removes control characters (char <= 32) from both + * ends of this String. + * </p> + * + * @see Pattern + * @see #trim(String) + * @see <a + * href="http://www.w3.org/TR/xpath/#function-normalize-space">http://www.w3.org/TR/xpath/#function-normalize-space</a> + * @param str the source String to normalize whitespaces from, may be null + * @return the modified string with whitespace normalized, {@code null} if null String input + * + * @since 3.0 + */ + public static String normalizeSpace(String str) { + if (str == null) { + return null; + } + return WHITESPACE_BLOCK.matcher(trim(str)).replaceAll(" "); + } + + /** + * <p>Check if a CharSequence ends with any of an array of specified strings.</p> + * + * <pre> + * StringUtils.endsWithAny(null, null) = false + * StringUtils.endsWithAny(null, new String[] {"abc"}) = false + * StringUtils.endsWithAny("abcxyz", null) = false + * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true + * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true + * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true + * </pre> + * + * @param string the CharSequence to check, may be null + * @param searchStrings the CharSequences to find, may be null or empty + * @return {@code true} if the CharSequence ends with any of the the prefixes, case insensitive, or + * both {@code null} + * @since 3.0 + */ + public static boolean endsWithAny(CharSequence string, CharSequence... searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (CharSequence searchString : searchStrings) { + if (StringUtils.endsWith(string, searchString)) { + return true; + } + } + return false; + } + }
\ No newline at end of file diff --git a/src/org/apache/commons/lang3/SystemUtils.java b/src/org/apache/commons/lang3/SystemUtils.java index c6fb73f..a3da7cd 100644 --- a/src/org/apache/commons/lang3/SystemUtils.java +++ b/src/org/apache/commons/lang3/SystemUtils.java @@ -1,1419 +1,1419 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.io.File;
-
-/**
- * <p>
- * Helpers for {@code java.lang.System}.
- * </p>
- * <p>
- * If a system property cannot be read due to security restrictions, the corresponding field in this class will be set
- * to {@code null} and a message will be written to {@code System.err}.
- * </p>
- * <p>
- * #ThreadSafe#
- * </p>
- *
- * @since 1.0
- * @version $Id: SystemUtils.java 1160564 2011-08-23 06:56:42Z bayard $
- */
-public class SystemUtils {
-
- /**
- * The prefix String for all Windows OS.
- */
- private static final String OS_NAME_WINDOWS_PREFIX = "Windows";
-
- // System property constants
- // -----------------------------------------------------------------------
- // These MUST be declared first. Other constants depend on this.
-
- /**
- * The System property key for the user home directory.
- */
- private static final String USER_HOME_KEY = "user.home";
-
- /**
- * The System property key for the user directory.
- */
- private static final String USER_DIR_KEY = "user.dir";
-
- /**
- * The System property key for the Java IO temporary directory.
- */
- private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir";
-
- /**
- * The System property key for the Java home directory.
- */
- private static final String JAVA_HOME_KEY = "java.home";
-
- /**
- * <p>
- * The {@code awt.toolkit} System Property.
- * </p>
- * <p>
- * Holds a class name, on Windows XP this is {@code sun.awt.windows.WToolkit}.
- * </p>
- * <p>
- * <b>On platforms without a GUI, this value is {@code null}.</b>
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- */
- public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit");
-
- /**
- * <p>
- * The {@code file.encoding} System Property.
- * </p>
- * <p>
- * File encoding, such as {@code Cp1252}.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.2
- */
- public static final String FILE_ENCODING = getSystemProperty("file.encoding");
-
- /**
- * <p>
- * The {@code file.separator} System Property. File separator (<code>"/"</code> on UNIX).
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String FILE_SEPARATOR = getSystemProperty("file.separator");
-
- /**
- * <p>
- * The {@code java.awt.fonts} System Property.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- */
- public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts");
-
- /**
- * <p>
- * The {@code java.awt.graphicsenv} System Property.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- */
- public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv");
-
- /**
- * <p>
- * The {@code java.awt.headless} System Property. The value of this property is the String {@code "true"} or
- * {@code "false"}.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @see #isJavaAwtHeadless()
- * @since 2.1
- * @since Java 1.4
- */
- public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless");
-
- /**
- * <p>
- * The {@code java.awt.printerjob} System Property.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- */
- public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob");
-
- /**
- * <p>
- * The {@code java.class.path} System Property. Java class path.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path");
-
- /**
- * <p>
- * The {@code java.class.version} System Property. Java class format version number.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version");
-
- /**
- * <p>
- * The {@code java.compiler} System Property. Name of JIT compiler to use. First in JDK version 1.2. Not used in Sun
- * JDKs after 1.2.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2. Not used in Sun versions after 1.2.
- */
- public static final String JAVA_COMPILER = getSystemProperty("java.compiler");
-
- /**
- * <p>
- * The {@code java.endorsed.dirs} System Property. Path of endorsed directory or directories.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.4
- */
- public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs");
-
- /**
- * <p>
- * The {@code java.ext.dirs} System Property. Path of extension directory or directories.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.3
- */
- public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs");
-
- /**
- * <p>
- * The {@code java.home} System Property. Java installation directory.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY);
-
- /**
- * <p>
- * The {@code java.io.tmpdir} System Property. Default temp file path.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY);
-
- /**
- * <p>
- * The {@code java.library.path} System Property. List of paths to search when loading libraries.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path");
-
- /**
- * <p>
- * The {@code java.runtime.name} System Property. Java Runtime Environment name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.3
- */
- public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name");
-
- /**
- * <p>
- * The {@code java.runtime.version} System Property. Java Runtime Environment version.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.3
- */
- public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version");
-
- /**
- * <p>
- * The {@code java.specification.name} System Property. Java Runtime Environment specification name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name");
-
- /**
- * <p>
- * The {@code java.specification.vendor} System Property. Java Runtime Environment specification vendor.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor");
-
- /**
- * <p>
- * The {@code java.specification.version} System Property. Java Runtime Environment specification version.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.3
- */
- public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version");
- private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION);
-
- /**
- * <p>
- * The {@code java.util.prefs.PreferencesFactory} System Property. A class name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- * @since Java 1.4
- */
- public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY =
- getSystemProperty("java.util.prefs.PreferencesFactory");
-
- /**
- * <p>
- * The {@code java.vendor} System Property. Java vendor-specific string.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_VENDOR = getSystemProperty("java.vendor");
-
- /**
- * <p>
- * The {@code java.vendor.url} System Property. Java vendor URL.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url");
-
- /**
- * <p>
- * The {@code java.version} System Property. Java version number.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String JAVA_VERSION = getSystemProperty("java.version");
-
- /**
- * <p>
- * The {@code java.vm.info} System Property. Java Virtual Machine implementation info.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.2
- */
- public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info");
-
- /**
- * <p>
- * The {@code java.vm.name} System Property. Java Virtual Machine implementation name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name");
-
- /**
- * <p>
- * The {@code java.vm.specification.name} System Property. Java Virtual Machine specification name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name");
-
- /**
- * <p>
- * The {@code java.vm.specification.vendor} System Property. Java Virtual Machine specification vendor.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor");
-
- /**
- * <p>
- * The {@code java.vm.specification.version} System Property. Java Virtual Machine specification version.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version");
-
- /**
- * <p>
- * The {@code java.vm.vendor} System Property. Java Virtual Machine implementation vendor.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor");
-
- /**
- * <p>
- * The {@code java.vm.version} System Property. Java Virtual Machine implementation version.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.2
- */
- public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version");
-
- /**
- * <p>
- * The {@code line.separator} System Property. Line separator (<code>"\n"</code> on UNIX).
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String LINE_SEPARATOR = getSystemProperty("line.separator");
-
- /**
- * <p>
- * The {@code os.arch} System Property. Operating system architecture.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String OS_ARCH = getSystemProperty("os.arch");
-
- /**
- * <p>
- * The {@code os.name} System Property. Operating system name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String OS_NAME = getSystemProperty("os.name");
-
- /**
- * <p>
- * The {@code os.version} System Property. Operating system version.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String OS_VERSION = getSystemProperty("os.version");
-
- /**
- * <p>
- * The {@code path.separator} System Property. Path separator (<code>":"</code> on UNIX).
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String PATH_SEPARATOR = getSystemProperty("path.separator");
-
- /**
- * <p>
- * The {@code user.country} or {@code user.region} System Property. User's country code, such as {@code GB}. First
- * in Java version 1.2 as {@code user.region}. Renamed to {@code user.country} in 1.4
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.2
- */
- public static final String USER_COUNTRY = getSystemProperty("user.country") == null ?
- getSystemProperty("user.region") : getSystemProperty("user.country");
-
- /**
- * <p>
- * The {@code user.dir} System Property. User's current working directory.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String USER_DIR = getSystemProperty(USER_DIR_KEY);
-
- /**
- * <p>
- * The {@code user.home} System Property. User's home directory.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String USER_HOME = getSystemProperty(USER_HOME_KEY);
-
- /**
- * <p>
- * The {@code user.language} System Property. User's language code, such as {@code "en"}.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.0
- * @since Java 1.2
- */
- public static final String USER_LANGUAGE = getSystemProperty("user.language");
-
- /**
- * <p>
- * The {@code user.name} System Property. User's account name.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since Java 1.1
- */
- public static final String USER_NAME = getSystemProperty("user.name");
-
- /**
- * <p>
- * The {@code user.timezone} System Property. For example: {@code "America/Los_Angeles"}.
- * </p>
- * <p>
- * Defaults to {@code null} if the runtime does not have security access to read this property or the property does
- * not exist.
- * </p>
- * <p>
- * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or
- * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of
- * sync with that System property.
- * </p>
- *
- * @since 2.1
- */
- public static final String USER_TIMEZONE = getSystemProperty("user.timezone");
-
- // Java version checks
- // -----------------------------------------------------------------------
- // These MUST be declared after those above as they depend on the
- // values being set up
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.1 (also 1.1.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.2 (also 1.2.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.3 (also 1.3.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.4 (also 1.4.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.5 (also 1.5.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.6 (also 1.6.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- */
- public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6");
-
- /**
- * <p>
- * Is {@code true} if this is Java version 1.7 (also 1.7.x versions).
- * </p>
- * <p>
- * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}.
- * </p>
- *
- * @since 3.0
- */
- public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7");
-
- // Operating system checks
- // -----------------------------------------------------------------------
- // These MUST be declared after those above as they depend on the
- // values being set up
- // OS names from http://www.vamphq.com/os.html
- // Selected ones included - please advise dev@commons.apache.org
- // if you want another added or a mistake corrected
-
- /**
- * <p>
- * Is {@code true} if this is AIX.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_AIX = getOSMatchesName("AIX");
-
- /**
- * <p>
- * Is {@code true} if this is HP-UX.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX");
-
- /**
- * <p>
- * Is {@code true} if this is Irix.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_IRIX = getOSMatchesName("Irix");
-
- /**
- * <p>
- * Is {@code true} if this is Linux.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_LINUX = getOSMatchesName("Linux") || getOSMatchesName("LINUX");
-
- /**
- * <p>
- * Is {@code true} if this is Mac.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_MAC = getOSMatchesName("Mac");
-
- /**
- * <p>
- * Is {@code true} if this is Mac.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X");
-
- /**
- * <p>
- * Is {@code true} if this is FreeBSD.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 3.0.2
- */
- public static final boolean IS_OS_FREE_BSD = getOSMatchesName("FreeBSD");
-
- /**
- * <p>
- * Is {@code true} if this is OpenBSD.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 3.0.2
- */
- public static final boolean IS_OS_OPEN_BSD = getOSMatchesName("OpenBSD");
-
- /**
- * <p>
- * Is {@code true} if this is NetBSD.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 3.0.2
- */
- public static final boolean IS_OS_NET_BSD = getOSMatchesName("NetBSD");
-
- /**
- * <p>
- * Is {@code true} if this is OS/2.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2");
-
- /**
- * <p>
- * Is {@code true} if this is Solaris.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris");
-
- /**
- * <p>
- * Is {@code true} if this is SunOS.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS");
-
- /**
- * <p>
- * Is {@code true} if this is a UNIX like system, as in any of AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.1
- */
- public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX
- || IS_OS_SOLARIS || IS_OS_SUN_OS || IS_OS_FREE_BSD || IS_OS_OPEN_BSD || IS_OS_NET_BSD;
-
- /**
- * <p>
- * Is {@code true} if this is Windows.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX);
-
- /**
- * <p>
- * Is {@code true} if this is Windows 2000.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_2000 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.0");
-
- /**
- * <p>
- * Is {@code true} if this is Windows 95.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_95 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.0");
- // Java 1.2 running on Windows98 returns 'Windows 95', hence the above
-
- /**
- * <p>
- * Is {@code true} if this is Windows 98.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_98 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.1");
- // Java 1.2 running on Windows98 returns 'Windows 95', hence the above
-
- /**
- * <p>
- * Is {@code true} if this is Windows ME.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_ME = getOSMatches(OS_NAME_WINDOWS_PREFIX, "4.9");
- // Java 1.2 running on WindowsME may return 'Windows 95', hence the above
-
- /**
- * <p>
- * Is {@code true} if this is Windows NT.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " NT");
- // Windows 2000 returns 'Windows 2000' but may suffer from same Java1.2 problem
-
- /**
- * <p>
- * Is {@code true} if this is Windows XP.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.0
- */
- public static final boolean IS_OS_WINDOWS_XP = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.1");
-
- // -----------------------------------------------------------------------
- /**
- * <p>
- * Is {@code true} if this is Windows Vista.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 2.4
- */
- public static final boolean IS_OS_WINDOWS_VISTA = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.0");
-
- /**
- * <p>
- * Is {@code true} if this is Windows 7.
- * </p>
- * <p>
- * The field will return {@code false} if {@code OS_NAME} is {@code null}.
- * </p>
- *
- * @since 3.0
- */
- public static final boolean IS_OS_WINDOWS_7 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.1");
-
- /**
- * <p>
- * Gets the Java home directory as a {@code File}.
- * </p>
- *
- * @return a directory
- * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
- * access to the specified system property.
- * @see System#getProperty(String)
- * @since 2.1
- */
- public static File getJavaHome() {
- return new File(System.getProperty(JAVA_HOME_KEY));
- }
-
- /**
- * <p>
- * Gets the Java IO temporary directory as a {@code File}.
- * </p>
- *
- * @return a directory
- * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
- * access to the specified system property.
- * @see System#getProperty(String)
- * @since 2.1
- */
- public static File getJavaIoTmpDir() {
- return new File(System.getProperty(JAVA_IO_TMPDIR_KEY));
- }
-
- /**
- * <p>
- * Decides if the Java version matches.
- * </p>
- *
- * @param versionPrefix the prefix for the java version
- * @return true if matches, or false if not or can't determine
- */
- private static boolean getJavaVersionMatches(String versionPrefix) {
- return isJavaVersionMatch(JAVA_SPECIFICATION_VERSION, versionPrefix);
- }
-
- /**
- * Decides if the operating system matches.
- *
- * @param osNamePrefix the prefix for the os name
- * @param osVersionPrefix the prefix for the version
- * @return true if matches, or false if not or can't determine
- */
- private static boolean getOSMatches(String osNamePrefix, String osVersionPrefix) {
- return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix);
- }
-
- /**
- * Decides if the operating system matches.
- *
- * @param osNamePrefix the prefix for the os name
- * @return true if matches, or false if not or can't determine
- */
- private static boolean getOSMatchesName(String osNamePrefix) {
- return isOSNameMatch(OS_NAME, osNamePrefix);
- }
-
- // -----------------------------------------------------------------------
- /**
- * <p>
- * Gets a System property, defaulting to {@code null} if the property cannot be read.
- * </p>
- * <p>
- * If a {@code SecurityException} is caught, the return value is {@code null} and a message is written to
- * {@code System.err}.
- * </p>
- *
- * @param property the system property name
- * @return the system property value or {@code null} if a security problem occurs
- */
- private static String getSystemProperty(String property) {
- try {
- return System.getProperty(property);
- } catch (SecurityException ex) {
- // we are not allowed to look at this property
- System.err.println("Caught a SecurityException reading the system property '" + property
- + "'; the SystemUtils property value will default to null.");
- return null;
- }
- }
-
- /**
- * <p>
- * Gets the user directory as a {@code File}.
- * </p>
- *
- * @return a directory
- * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
- * access to the specified system property.
- * @see System#getProperty(String)
- * @since 2.1
- */
- public static File getUserDir() {
- return new File(System.getProperty(USER_DIR_KEY));
- }
-
- /**
- * <p>
- * Gets the user home directory as a {@code File}.
- * </p>
- *
- * @return a directory
- * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow
- * access to the specified system property.
- * @see System#getProperty(String)
- * @since 2.1
- */
- public static File getUserHome() {
- return new File(System.getProperty(USER_HOME_KEY));
- }
-
- /**
- * Returns whether the {@link #JAVA_AWT_HEADLESS} value is {@code true}.
- *
- * @return {@code true} if {@code JAVA_AWT_HEADLESS} is {@code "true"}, {@code false} otherwise.
- * @see #JAVA_AWT_HEADLESS
- * @since 2.1
- * @since Java 1.4
- */
- public static boolean isJavaAwtHeadless() {
- return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS.equals(Boolean.TRUE.toString()) : false;
- }
-
- /**
- * <p>
- * Is the Java version at least the requested version.
- * </p>
- * <p>
- * Example input:
- * </p>
- * <ul>
- * <li>{@code 1.2f} to test for Java 1.2</li>
- * <li>{@code 1.31f} to test for Java 1.3.1</li>
- * </ul>
- *
- * @param requiredVersion the required version, for example 1.31f
- * @return {@code true} if the actual version is equal or greater than the required version
- */
- public static boolean isJavaVersionAtLeast(JavaVersion requiredVersion) {
- return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion);
- }
-
- /**
- * <p>
- * Decides if the Java version matches.
- * </p>
- * <p>
- * This method is package private instead of private to support unit test invocation.
- * </p>
- *
- * @param version the actual Java version
- * @param versionPrefix the prefix for the expected Java version
- * @return true if matches, or false if not or can't determine
- */
- static boolean isJavaVersionMatch(String version, String versionPrefix) {
- if (version == null) {
- return false;
- }
- return version.startsWith(versionPrefix);
- }
-
- /**
- * Decides if the operating system matches.
- * <p>
- * This method is package private instead of private to support unit test invocation.
- * </p>
- *
- * @param osName the actual OS name
- * @param osVersion the actual OS version
- * @param osNamePrefix the prefix for the expected OS name
- * @param osVersionPrefix the prefix for the expected OS version
- * @return true if matches, or false if not or can't determine
- */
- static boolean isOSMatch(String osName, String osVersion, String osNamePrefix, String osVersionPrefix) {
- if (osName == null || osVersion == null) {
- return false;
- }
- return osName.startsWith(osNamePrefix) && osVersion.startsWith(osVersionPrefix);
- }
-
- /**
- * Decides if the operating system matches.
- * <p>
- * This method is package private instead of private to support unit test invocation.
- * </p>
- *
- * @param osName the actual OS name
- * @param osNamePrefix the prefix for the expected OS name
- * @return true if matches, or false if not or can't determine
- */
- static boolean isOSNameMatch(String osName, String osNamePrefix) {
- if (osName == null) {
- return false;
- }
- return osName.startsWith(osNamePrefix);
- }
-
- // -----------------------------------------------------------------------
- /**
- * <p>
- * SystemUtils instances should NOT be constructed in standard programming. Instead, the class should be used as
- * {@code SystemUtils.FILE_SEPARATOR}.
- * </p>
- * <p>
- * This constructor is public to permit tools that require a JavaBean instance to operate.
- * </p>
- */
- public SystemUtils() {
- super();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.File; + +/** + * <p> + * Helpers for {@code java.lang.System}. + * </p> + * <p> + * If a system property cannot be read due to security restrictions, the corresponding field in this class will be set + * to {@code null} and a message will be written to {@code System.err}. + * </p> + * <p> + * #ThreadSafe# + * </p> + * + * @since 1.0 + * @version $Id: SystemUtils.java 1160564 2011-08-23 06:56:42Z bayard $ + */ +public class SystemUtils { + + /** + * The prefix String for all Windows OS. + */ + private static final String OS_NAME_WINDOWS_PREFIX = "Windows"; + + // System property constants + // ----------------------------------------------------------------------- + // These MUST be declared first. Other constants depend on this. + + /** + * The System property key for the user home directory. + */ + private static final String USER_HOME_KEY = "user.home"; + + /** + * The System property key for the user directory. + */ + private static final String USER_DIR_KEY = "user.dir"; + + /** + * The System property key for the Java IO temporary directory. + */ + private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir"; + + /** + * The System property key for the Java home directory. + */ + private static final String JAVA_HOME_KEY = "java.home"; + + /** + * <p> + * The {@code awt.toolkit} System Property. + * </p> + * <p> + * Holds a class name, on Windows XP this is {@code sun.awt.windows.WToolkit}. + * </p> + * <p> + * <b>On platforms without a GUI, this value is {@code null}.</b> + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + */ + public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit"); + + /** + * <p> + * The {@code file.encoding} System Property. + * </p> + * <p> + * File encoding, such as {@code Cp1252}. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.2 + */ + public static final String FILE_ENCODING = getSystemProperty("file.encoding"); + + /** + * <p> + * The {@code file.separator} System Property. File separator (<code>"/"</code> on UNIX). + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String FILE_SEPARATOR = getSystemProperty("file.separator"); + + /** + * <p> + * The {@code java.awt.fonts} System Property. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + */ + public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts"); + + /** + * <p> + * The {@code java.awt.graphicsenv} System Property. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + */ + public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv"); + + /** + * <p> + * The {@code java.awt.headless} System Property. The value of this property is the String {@code "true"} or + * {@code "false"}. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @see #isJavaAwtHeadless() + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless"); + + /** + * <p> + * The {@code java.awt.printerjob} System Property. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + */ + public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob"); + + /** + * <p> + * The {@code java.class.path} System Property. Java class path. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path"); + + /** + * <p> + * The {@code java.class.version} System Property. Java class format version number. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version"); + + /** + * <p> + * The {@code java.compiler} System Property. Name of JIT compiler to use. First in JDK version 1.2. Not used in Sun + * JDKs after 1.2. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2. Not used in Sun versions after 1.2. + */ + public static final String JAVA_COMPILER = getSystemProperty("java.compiler"); + + /** + * <p> + * The {@code java.endorsed.dirs} System Property. Path of endorsed directory or directories. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.4 + */ + public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs"); + + /** + * <p> + * The {@code java.ext.dirs} System Property. Path of extension directory or directories. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.3 + */ + public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs"); + + /** + * <p> + * The {@code java.home} System Property. Java installation directory. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY); + + /** + * <p> + * The {@code java.io.tmpdir} System Property. Default temp file path. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY); + + /** + * <p> + * The {@code java.library.path} System Property. List of paths to search when loading libraries. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path"); + + /** + * <p> + * The {@code java.runtime.name} System Property. Java Runtime Environment name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.3 + */ + public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name"); + + /** + * <p> + * The {@code java.runtime.version} System Property. Java Runtime Environment version. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.3 + */ + public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version"); + + /** + * <p> + * The {@code java.specification.name} System Property. Java Runtime Environment specification name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name"); + + /** + * <p> + * The {@code java.specification.vendor} System Property. Java Runtime Environment specification vendor. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor"); + + /** + * <p> + * The {@code java.specification.version} System Property. Java Runtime Environment specification version. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.3 + */ + public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); + private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION); + + /** + * <p> + * The {@code java.util.prefs.PreferencesFactory} System Property. A class name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY = + getSystemProperty("java.util.prefs.PreferencesFactory"); + + /** + * <p> + * The {@code java.vendor} System Property. Java vendor-specific string. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_VENDOR = getSystemProperty("java.vendor"); + + /** + * <p> + * The {@code java.vendor.url} System Property. Java vendor URL. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url"); + + /** + * <p> + * The {@code java.version} System Property. Java version number. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String JAVA_VERSION = getSystemProperty("java.version"); + + /** + * <p> + * The {@code java.vm.info} System Property. Java Virtual Machine implementation info. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.2 + */ + public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info"); + + /** + * <p> + * The {@code java.vm.name} System Property. Java Virtual Machine implementation name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name"); + + /** + * <p> + * The {@code java.vm.specification.name} System Property. Java Virtual Machine specification name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name"); + + /** + * <p> + * The {@code java.vm.specification.vendor} System Property. Java Virtual Machine specification vendor. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor"); + + /** + * <p> + * The {@code java.vm.specification.version} System Property. Java Virtual Machine specification version. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version"); + + /** + * <p> + * The {@code java.vm.vendor} System Property. Java Virtual Machine implementation vendor. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor"); + + /** + * <p> + * The {@code java.vm.version} System Property. Java Virtual Machine implementation version. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.2 + */ + public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version"); + + /** + * <p> + * The {@code line.separator} System Property. Line separator (<code>"\n"</code> on UNIX). + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String LINE_SEPARATOR = getSystemProperty("line.separator"); + + /** + * <p> + * The {@code os.arch} System Property. Operating system architecture. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String OS_ARCH = getSystemProperty("os.arch"); + + /** + * <p> + * The {@code os.name} System Property. Operating system name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String OS_NAME = getSystemProperty("os.name"); + + /** + * <p> + * The {@code os.version} System Property. Operating system version. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String OS_VERSION = getSystemProperty("os.version"); + + /** + * <p> + * The {@code path.separator} System Property. Path separator (<code>":"</code> on UNIX). + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String PATH_SEPARATOR = getSystemProperty("path.separator"); + + /** + * <p> + * The {@code user.country} or {@code user.region} System Property. User's country code, such as {@code GB}. First + * in Java version 1.2 as {@code user.region}. Renamed to {@code user.country} in 1.4 + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.2 + */ + public static final String USER_COUNTRY = getSystemProperty("user.country") == null ? + getSystemProperty("user.region") : getSystemProperty("user.country"); + + /** + * <p> + * The {@code user.dir} System Property. User's current working directory. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String USER_DIR = getSystemProperty(USER_DIR_KEY); + + /** + * <p> + * The {@code user.home} System Property. User's home directory. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String USER_HOME = getSystemProperty(USER_HOME_KEY); + + /** + * <p> + * The {@code user.language} System Property. User's language code, such as {@code "en"}. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.0 + * @since Java 1.2 + */ + public static final String USER_LANGUAGE = getSystemProperty("user.language"); + + /** + * <p> + * The {@code user.name} System Property. User's account name. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since Java 1.1 + */ + public static final String USER_NAME = getSystemProperty("user.name"); + + /** + * <p> + * The {@code user.timezone} System Property. For example: {@code "America/Los_Angeles"}. + * </p> + * <p> + * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + * </p> + * <p> + * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + * </p> + * + * @since 2.1 + */ + public static final String USER_TIMEZONE = getSystemProperty("user.timezone"); + + // Java version checks + // ----------------------------------------------------------------------- + // These MUST be declared after those above as they depend on the + // values being set up + + /** + * <p> + * Is {@code true} if this is Java version 1.1 (also 1.1.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1"); + + /** + * <p> + * Is {@code true} if this is Java version 1.2 (also 1.2.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2"); + + /** + * <p> + * Is {@code true} if this is Java version 1.3 (also 1.3.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3"); + + /** + * <p> + * Is {@code true} if this is Java version 1.4 (also 1.4.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4"); + + /** + * <p> + * Is {@code true} if this is Java version 1.5 (also 1.5.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5"); + + /** + * <p> + * Is {@code true} if this is Java version 1.6 (also 1.6.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + */ + public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6"); + + /** + * <p> + * Is {@code true} if this is Java version 1.7 (also 1.7.x versions). + * </p> + * <p> + * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + * </p> + * + * @since 3.0 + */ + public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7"); + + // Operating system checks + // ----------------------------------------------------------------------- + // These MUST be declared after those above as they depend on the + // values being set up + // OS names from http://www.vamphq.com/os.html + // Selected ones included - please advise dev@commons.apache.org + // if you want another added or a mistake corrected + + /** + * <p> + * Is {@code true} if this is AIX. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_AIX = getOSMatchesName("AIX"); + + /** + * <p> + * Is {@code true} if this is HP-UX. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX"); + + /** + * <p> + * Is {@code true} if this is Irix. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_IRIX = getOSMatchesName("Irix"); + + /** + * <p> + * Is {@code true} if this is Linux. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_LINUX = getOSMatchesName("Linux") || getOSMatchesName("LINUX"); + + /** + * <p> + * Is {@code true} if this is Mac. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_MAC = getOSMatchesName("Mac"); + + /** + * <p> + * Is {@code true} if this is Mac. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X"); + + /** + * <p> + * Is {@code true} if this is FreeBSD. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 3.0.2 + */ + public static final boolean IS_OS_FREE_BSD = getOSMatchesName("FreeBSD"); + + /** + * <p> + * Is {@code true} if this is OpenBSD. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 3.0.2 + */ + public static final boolean IS_OS_OPEN_BSD = getOSMatchesName("OpenBSD"); + + /** + * <p> + * Is {@code true} if this is NetBSD. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 3.0.2 + */ + public static final boolean IS_OS_NET_BSD = getOSMatchesName("NetBSD"); + + /** + * <p> + * Is {@code true} if this is OS/2. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2"); + + /** + * <p> + * Is {@code true} if this is Solaris. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris"); + + /** + * <p> + * Is {@code true} if this is SunOS. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS"); + + /** + * <p> + * Is {@code true} if this is a UNIX like system, as in any of AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.1 + */ + public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX + || IS_OS_SOLARIS || IS_OS_SUN_OS || IS_OS_FREE_BSD || IS_OS_OPEN_BSD || IS_OS_NET_BSD; + + /** + * <p> + * Is {@code true} if this is Windows. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX); + + /** + * <p> + * Is {@code true} if this is Windows 2000. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_2000 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.0"); + + /** + * <p> + * Is {@code true} if this is Windows 95. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_95 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.0"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above + + /** + * <p> + * Is {@code true} if this is Windows 98. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_98 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.1"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above + + /** + * <p> + * Is {@code true} if this is Windows ME. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_ME = getOSMatches(OS_NAME_WINDOWS_PREFIX, "4.9"); + // Java 1.2 running on WindowsME may return 'Windows 95', hence the above + + /** + * <p> + * Is {@code true} if this is Windows NT. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " NT"); + // Windows 2000 returns 'Windows 2000' but may suffer from same Java1.2 problem + + /** + * <p> + * Is {@code true} if this is Windows XP. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_XP = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.1"); + + // ----------------------------------------------------------------------- + /** + * <p> + * Is {@code true} if this is Windows Vista. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 2.4 + */ + public static final boolean IS_OS_WINDOWS_VISTA = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.0"); + + /** + * <p> + * Is {@code true} if this is Windows 7. + * </p> + * <p> + * The field will return {@code false} if {@code OS_NAME} is {@code null}. + * </p> + * + * @since 3.0 + */ + public static final boolean IS_OS_WINDOWS_7 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.1"); + + /** + * <p> + * Gets the Java home directory as a {@code File}. + * </p> + * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaHome() { + return new File(System.getProperty(JAVA_HOME_KEY)); + } + + /** + * <p> + * Gets the Java IO temporary directory as a {@code File}. + * </p> + * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaIoTmpDir() { + return new File(System.getProperty(JAVA_IO_TMPDIR_KEY)); + } + + /** + * <p> + * Decides if the Java version matches. + * </p> + * + * @param versionPrefix the prefix for the java version + * @return true if matches, or false if not or can't determine + */ + private static boolean getJavaVersionMatches(String versionPrefix) { + return isJavaVersionMatch(JAVA_SPECIFICATION_VERSION, versionPrefix); + } + + /** + * Decides if the operating system matches. + * + * @param osNamePrefix the prefix for the os name + * @param osVersionPrefix the prefix for the version + * @return true if matches, or false if not or can't determine + */ + private static boolean getOSMatches(String osNamePrefix, String osVersionPrefix) { + return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix); + } + + /** + * Decides if the operating system matches. + * + * @param osNamePrefix the prefix for the os name + * @return true if matches, or false if not or can't determine + */ + private static boolean getOSMatchesName(String osNamePrefix) { + return isOSNameMatch(OS_NAME, osNamePrefix); + } + + // ----------------------------------------------------------------------- + /** + * <p> + * Gets a System property, defaulting to {@code null} if the property cannot be read. + * </p> + * <p> + * If a {@code SecurityException} is caught, the return value is {@code null} and a message is written to + * {@code System.err}. + * </p> + * + * @param property the system property name + * @return the system property value or {@code null} if a security problem occurs + */ + private static String getSystemProperty(String property) { + try { + return System.getProperty(property); + } catch (SecurityException ex) { + // we are not allowed to look at this property + System.err.println("Caught a SecurityException reading the system property '" + property + + "'; the SystemUtils property value will default to null."); + return null; + } + } + + /** + * <p> + * Gets the user directory as a {@code File}. + * </p> + * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserDir() { + return new File(System.getProperty(USER_DIR_KEY)); + } + + /** + * <p> + * Gets the user home directory as a {@code File}. + * </p> + * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserHome() { + return new File(System.getProperty(USER_HOME_KEY)); + } + + /** + * Returns whether the {@link #JAVA_AWT_HEADLESS} value is {@code true}. + * + * @return {@code true} if {@code JAVA_AWT_HEADLESS} is {@code "true"}, {@code false} otherwise. + * @see #JAVA_AWT_HEADLESS + * @since 2.1 + * @since Java 1.4 + */ + public static boolean isJavaAwtHeadless() { + return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS.equals(Boolean.TRUE.toString()) : false; + } + + /** + * <p> + * Is the Java version at least the requested version. + * </p> + * <p> + * Example input: + * </p> + * <ul> + * <li>{@code 1.2f} to test for Java 1.2</li> + * <li>{@code 1.31f} to test for Java 1.3.1</li> + * </ul> + * + * @param requiredVersion the required version, for example 1.31f + * @return {@code true} if the actual version is equal or greater than the required version + */ + public static boolean isJavaVersionAtLeast(JavaVersion requiredVersion) { + return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion); + } + + /** + * <p> + * Decides if the Java version matches. + * </p> + * <p> + * This method is package private instead of private to support unit test invocation. + * </p> + * + * @param version the actual Java version + * @param versionPrefix the prefix for the expected Java version + * @return true if matches, or false if not or can't determine + */ + static boolean isJavaVersionMatch(String version, String versionPrefix) { + if (version == null) { + return false; + } + return version.startsWith(versionPrefix); + } + + /** + * Decides if the operating system matches. + * <p> + * This method is package private instead of private to support unit test invocation. + * </p> + * + * @param osName the actual OS name + * @param osVersion the actual OS version + * @param osNamePrefix the prefix for the expected OS name + * @param osVersionPrefix the prefix for the expected OS version + * @return true if matches, or false if not or can't determine + */ + static boolean isOSMatch(String osName, String osVersion, String osNamePrefix, String osVersionPrefix) { + if (osName == null || osVersion == null) { + return false; + } + return osName.startsWith(osNamePrefix) && osVersion.startsWith(osVersionPrefix); + } + + /** + * Decides if the operating system matches. + * <p> + * This method is package private instead of private to support unit test invocation. + * </p> + * + * @param osName the actual OS name + * @param osNamePrefix the prefix for the expected OS name + * @return true if matches, or false if not or can't determine + */ + static boolean isOSNameMatch(String osName, String osNamePrefix) { + if (osName == null) { + return false; + } + return osName.startsWith(osNamePrefix); + } + + // ----------------------------------------------------------------------- + /** + * <p> + * SystemUtils instances should NOT be constructed in standard programming. Instead, the class should be used as + * {@code SystemUtils.FILE_SEPARATOR}. + * </p> + * <p> + * This constructor is public to permit tools that require a JavaBean instance to operate. + * </p> + */ + public SystemUtils() { + super(); + } + +} diff --git a/src/org/apache/commons/lang3/Validate.java b/src/org/apache/commons/lang3/Validate.java index 36716d6..4c1fd4f 100644 --- a/src/org/apache/commons/lang3/Validate.java +++ b/src/org/apache/commons/lang3/Validate.java @@ -1,1071 +1,1071 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3;
-
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-/**
- * <p>This class assists in validating arguments. The validation methods are
- * based along the following principles:
- * <ul>
- * <li>An invalid {@code null} argument causes a {@link NullPointerException}.</li>
- * <li>A non-{@code null} argument causes an {@link IllegalArgumentException}.</li>
- * <li>An invalid index into an array/collection/map/string causes an {@link IndexOutOfBoundsException}.</li>
- * </ul>
- *
- * <p>All exceptions messages are
- * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html#syntax">format strings</a>
- * as defined by the Java platform. For example:</p>
- *
- * <pre>
- * Validate.isTrue(i > 0, "The value must be greater than zero: %d", i);
- * Validate.notNull(surname, "The surname must not be %s", null);
- * </pre>
- *
- * <p>#ThreadSafe#</p>
- * @version $Id: Validate.java 1153490 2011-08-03 13:53:35Z ggregory $
- * @see java.lang.String#format(String, Object...)
- * @since 2.0
- */
-public class Validate {
-
- private static final String DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE =
- "The value %s is not in the specified exclusive range of %s to %s";
- private static final String DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE =
- "The value %s is not in the specified inclusive range of %s to %s";
- private static final String DEFAULT_MATCHES_PATTERN_EX = "The string %s does not match the pattern %s";
- private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null";
- private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false";
- private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE =
- "The validated array contains null element at index: %d";
- private static final String DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE =
- "The validated collection contains null element at index: %d";
- private static final String DEFAULT_NOT_BLANK_EX_MESSAGE = "The validated character sequence is blank";
- private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty";
- private static final String DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE =
- "The validated character sequence is empty";
- private static final String DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE = "The validated collection is empty";
- private static final String DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE = "The validated map is empty";
- private static final String DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE = "The validated array index is invalid: %d";
- private static final String DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE =
- "The validated character sequence index is invalid: %d";
- private static final String DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE =
- "The validated collection index is invalid: %d";
- private static final String DEFAULT_VALID_STATE_EX_MESSAGE = "The validated state is false";
- private static final String DEFAULT_IS_ASSIGNABLE_EX_MESSAGE =
- "The validated class can not be converted to the %s class";
- private static final String DEFAULT_IS_INSTANCE_OF_EX_MESSAGE = "The validated object is not an instance of %s";
-
- /**
- * Constructor. This class should not normally be instantiated.
- */
- public Validate() {
- super();
- }
-
- // isTrue
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the argument condition is {@code true}; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating according to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);</pre>
- *
- * <p>For performance reasons, the long value is passed as a separate parameter and
- * appended to the exception message only in the case of an error.</p>
- *
- * @param expression the boolean expression to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param value the value to append to the message when invalid
- * @throws IllegalArgumentException if expression is {@code false}
- * @see #isTrue(boolean)
- * @see #isTrue(boolean, String, double)
- * @see #isTrue(boolean, String, Object...)
- */
- public static void isTrue(boolean expression, String message, long value) {
- if (expression == false) {
- throw new IllegalArgumentException(String.format(message, Long.valueOf(value)));
- }
- }
-
- /**
- * <p>Validate that the argument condition is {@code true}; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating according to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);</pre>
- *
- * <p>For performance reasons, the double value is passed as a separate parameter and
- * appended to the exception message only in the case of an error.</p>
- *
- * @param expression the boolean expression to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param value the value to append to the message when invalid
- * @throws IllegalArgumentException if expression is {@code false}
- * @see #isTrue(boolean)
- * @see #isTrue(boolean, String, long)
- * @see #isTrue(boolean, String, Object...)
- */
- public static void isTrue(boolean expression, String message, double value) {
- if (expression == false) {
- throw new IllegalArgumentException(String.format(message, Double.valueOf(value)));
- }
- }
-
- /**
- * <p>Validate that the argument condition is {@code true}; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating according to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>
- * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max);
- * Validate.isTrue(myObject.isOk(), "The object is not okay");</pre>
- *
- * @param expression the boolean expression to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if expression is {@code false}
- * @see #isTrue(boolean)
- * @see #isTrue(boolean, String, long)
- * @see #isTrue(boolean, String, double)
- */
- public static void isTrue(boolean expression, String message, Object... values) {
- if (expression == false) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
- /**
- * <p>Validate that the argument condition is {@code true}; otherwise
- * throwing an exception. This method is useful when validating according
- * to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>
- * Validate.isTrue(i > 0);
- * Validate.isTrue(myObject.isOk());</pre>
- *
- * <p>The message of the exception is "The validated expression is
- * false".</p>
- *
- * @param expression the boolean expression to check
- * @throws IllegalArgumentException if expression is {@code false}
- * @see #isTrue(boolean, String, long)
- * @see #isTrue(boolean, String, double)
- * @see #isTrue(boolean, String, Object...)
- */
- public static void isTrue(boolean expression) {
- if (expression == false) {
- throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE);
- }
- }
-
- // notNull
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument is not {@code null};
- * otherwise throwing an exception.
- *
- * <pre>Validate.notNull(myObject, "The object must not be null");</pre>
- *
- * <p>The message of the exception is "The validated object is
- * null".</p>
- *
- * @param <T> the object type
- * @param object the object to check
- * @return the validated object (never {@code null} for method chaining)
- * @throws NullPointerException if the object is {@code null}
- * @see #notNull(Object, String, Object...)
- */
- public static <T> T notNull(T object) {
- return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE);
- }
-
- /**
- * <p>Validate that the specified argument is not {@code null};
- * otherwise throwing an exception with the specified message.
- *
- * <pre>Validate.notNull(myObject, "The object must not be null");</pre>
- *
- * @param <T> the object type
- * @param object the object to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message
- * @return the validated object (never {@code null} for method chaining)
- * @throws NullPointerException if the object is {@code null}
- * @see #notNull(Object)
- */
- public static <T> T notNull(T object, String message, Object... values) {
- if (object == null) {
- throw new NullPointerException(String.format(message, values));
- }
- return object;
- }
-
- // notEmpty array
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument array is neither {@code null}
- * nor a length of zero (no elements); otherwise throwing an exception
- * with the specified message.
- *
- * <pre>Validate.notEmpty(myArray, "The array must not be empty");</pre>
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated array (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if the array is empty
- * @see #notEmpty(Object[])
- */
- public static <T> T[] notEmpty(T[] array, String message, Object... values) {
- if (array == null) {
- throw new NullPointerException(String.format(message, values));
- }
- if (array.length == 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- return array;
- }
-
- /**
- * <p>Validate that the specified argument array is neither {@code null}
- * nor a length of zero (no elements); otherwise throwing an exception.
- *
- * <pre>Validate.notEmpty(myArray);</pre>
- *
- * <p>The message in the exception is "The validated array is
- * empty".
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @return the validated array (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if the array is empty
- * @see #notEmpty(Object[], String, Object...)
- */
- public static <T> T[] notEmpty(T[] array) {
- return notEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE);
- }
-
- // notEmpty collection
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument collection is neither {@code null}
- * nor a size of zero (no elements); otherwise throwing an exception
- * with the specified message.
- *
- * <pre>Validate.notEmpty(myCollection, "The collection must not be empty");</pre>
- *
- * @param <T> the collection type
- * @param collection the collection to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated collection (never {@code null} method for chaining)
- * @throws NullPointerException if the collection is {@code null}
- * @throws IllegalArgumentException if the collection is empty
- * @see #notEmpty(Object[])
- */
- public static <T extends Collection<?>> T notEmpty(T collection, String message, Object... values) {
- if (collection == null) {
- throw new NullPointerException(String.format(message, values));
- }
- if (collection.size() == 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- return collection;
- }
-
- /**
- * <p>Validate that the specified argument collection is neither {@code null}
- * nor a size of zero (no elements); otherwise throwing an exception.
- *
- * <pre>Validate.notEmpty(myCollection);</pre>
- *
- * <p>The message in the exception is "The validated collection is
- * empty".</p>
- *
- * @param <T> the collection type
- * @param collection the collection to check, validated not null by this method
- * @return the validated collection (never {@code null} method for chaining)
- * @throws NullPointerException if the collection is {@code null}
- * @throws IllegalArgumentException if the collection is empty
- * @see #notEmpty(Collection, String, Object...)
- */
- public static <T extends Collection<?>> T notEmpty(T collection) {
- return notEmpty(collection, DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE);
- }
-
- // notEmpty map
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument map is neither {@code null}
- * nor a size of zero (no elements); otherwise throwing an exception
- * with the specified message.
- *
- * <pre>Validate.notEmpty(myMap, "The map must not be empty");</pre>
- *
- * @param <T> the map type
- * @param map the map to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated map (never {@code null} method for chaining)
- * @throws NullPointerException if the map is {@code null}
- * @throws IllegalArgumentException if the map is empty
- * @see #notEmpty(Object[])
- */
- public static <T extends Map<?, ?>> T notEmpty(T map, String message, Object... values) {
- if (map == null) {
- throw new NullPointerException(String.format(message, values));
- }
- if (map.size() == 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- return map;
- }
-
- /**
- * <p>Validate that the specified argument map is neither {@code null}
- * nor a size of zero (no elements); otherwise throwing an exception.
- *
- * <pre>Validate.notEmpty(myMap);</pre>
- *
- * <p>The message in the exception is "The validated map is
- * empty".</p>
- *
- * @param <T> the map type
- * @param map the map to check, validated not null by this method
- * @return the validated map (never {@code null} method for chaining)
- * @throws NullPointerException if the map is {@code null}
- * @throws IllegalArgumentException if the map is empty
- * @see #notEmpty(Map, String, Object...)
- */
- public static <T extends Map<?, ?>> T notEmpty(T map) {
- return notEmpty(map, DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE);
- }
-
- // notEmpty string
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument character sequence is
- * neither {@code null} nor a length of zero (no characters);
- * otherwise throwing an exception with the specified message.
- *
- * <pre>Validate.notEmpty(myString, "The string must not be empty");</pre>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated character sequence (never {@code null} method for chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IllegalArgumentException if the character sequence is empty
- * @see #notEmpty(CharSequence)
- */
- public static <T extends CharSequence> T notEmpty(T chars, String message, Object... values) {
- if (chars == null) {
- throw new NullPointerException(String.format(message, values));
- }
- if (chars.length() == 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- return chars;
- }
-
- /**
- * <p>Validate that the specified argument character sequence is
- * neither {@code null} nor a length of zero (no characters);
- * otherwise throwing an exception with the specified message.
- *
- * <pre>Validate.notEmpty(myString);</pre>
- *
- * <p>The message in the exception is "The validated
- * character sequence is empty".</p>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @return the validated character sequence (never {@code null} method for chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IllegalArgumentException if the character sequence is empty
- * @see #notEmpty(CharSequence, String, Object...)
- */
- public static <T extends CharSequence> T notEmpty(T chars) {
- return notEmpty(chars, DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE);
- }
-
- // notBlank string
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument character sequence is
- * neither {@code null}, a length of zero (no characters), empty
- * nor whitespace; otherwise throwing an exception with the specified
- * message.
- *
- * <pre>Validate.notBlank(myString, "The string must not be blank");</pre>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated character sequence (never {@code null} method for chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IllegalArgumentException if the character sequence is blank
- * @see #notBlank(CharSequence)
- *
- * @since 3.0
- */
- public static <T extends CharSequence> T notBlank(T chars, String message, Object... values) {
- if (chars == null) {
- throw new NullPointerException(String.format(message, values));
- }
- if (StringUtils.isBlank(chars)) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- return chars;
- }
-
- /**
- * <p>Validate that the specified argument character sequence is
- * neither {@code null}, a length of zero (no characters), empty
- * nor whitespace; otherwise throwing an exception.
- *
- * <pre>Validate.notBlank(myString);</pre>
- *
- * <p>The message in the exception is "The validated character
- * sequence is blank".</p>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @return the validated character sequence (never {@code null} method for chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IllegalArgumentException if the character sequence is blank
- * @see #notBlank(CharSequence, String, Object...)
- *
- * @since 3.0
- */
- public static <T extends CharSequence> T notBlank(T chars) {
- return notBlank(chars, DEFAULT_NOT_BLANK_EX_MESSAGE);
- }
-
- // noNullElements array
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument array is neither
- * {@code null} nor contains any elements that are {@code null};
- * otherwise throwing an exception with the specified message.
- *
- * <pre>Validate.noNullElements(myArray, "The array contain null at position %d");</pre>
- *
- * <p>If the array is {@code null}, then the message in the exception
- * is "The validated object is null".</p>
- *
- * <p>If the array has a {@code null} element, then the iteration
- * index of the invalid element is appended to the {@code values}
- * argument.</p>
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated array (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if an element is {@code null}
- * @see #noNullElements(Object[])
- */
- public static <T> T[] noNullElements(T[] array, String message, Object... values) {
- Validate.notNull(array);
- for (int i = 0; i < array.length; i++) {
- if (array[i] == null) {
- Object[] values2 = ArrayUtils.add(values, Integer.valueOf(i));
- throw new IllegalArgumentException(String.format(message, values2));
- }
- }
- return array;
- }
-
- /**
- * <p>Validate that the specified argument array is neither
- * {@code null} nor contains any elements that are {@code null};
- * otherwise throwing an exception.
- *
- * <pre>Validate.noNullElements(myArray);</pre>
- *
- * <p>If the array is {@code null}, then the message in the exception
- * is "The validated object is null".</p>
- *
- * <p>If the array has a {@code null} element, then the message in the
- * exception is "The validated array contains null element at index:
- * " followed by the index.</p>
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @return the validated array (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if an element is {@code null}
- * @see #noNullElements(Object[], String, Object...)
- */
- public static <T> T[] noNullElements(T[] array) {
- return noNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE);
- }
-
- // noNullElements iterable
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument iterable is neither
- * {@code null} nor contains any elements that are {@code null};
- * otherwise throwing an exception with the specified message.
- *
- * <pre>Validate.noNullElements(myCollection, "The collection contains null at position %d");</pre>
- *
- * <p>If the iterable is {@code null}, then the message in the exception
- * is "The validated object is null".</p>
- *
- * <p>If the iterable has a {@code null} element, then the iteration
- * index of the invalid element is appended to the {@code values}
- * argument.</p>
- *
- * @param <T> the iterable type
- * @param iterable the iterable to check, validated not null by this method
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated iterable (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if an element is {@code null}
- * @see #noNullElements(Iterable)
- */
- public static <T extends Iterable<?>> T noNullElements(T iterable, String message, Object... values) {
- Validate.notNull(iterable);
- int i = 0;
- for (Iterator<?> it = iterable.iterator(); it.hasNext(); i++) {
- if (it.next() == null) {
- Object[] values2 = ArrayUtils.addAll(values, Integer.valueOf(i));
- throw new IllegalArgumentException(String.format(message, values2));
- }
- }
- return iterable;
- }
-
- /**
- * <p>Validate that the specified argument iterable is neither
- * {@code null} nor contains any elements that are {@code null};
- * otherwise throwing an exception.
- *
- * <pre>Validate.noNullElements(myCollection);</pre>
- *
- * <p>If the iterable is {@code null}, then the message in the exception
- * is "The validated object is null".</p>
- *
- * <p>If the array has a {@code null} element, then the message in the
- * exception is "The validated iterable contains null element at index:
- * " followed by the index.</p>
- *
- * @param <T> the iterable type
- * @param iterable the iterable to check, validated not null by this method
- * @return the validated iterable (never {@code null} method for chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IllegalArgumentException if an element is {@code null}
- * @see #noNullElements(Iterable, String, Object...)
- */
- public static <T extends Iterable<?>> T noNullElements(T iterable) {
- return noNullElements(iterable, DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE);
- }
-
- // validIndex array
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * array; otherwise throwing an exception with the specified message.</p>
- *
- * <pre>Validate.validIndex(myArray, 2, "The array index is invalid: ");</pre>
- *
- * <p>If the array is {@code null}, then the message of the exception
- * is "The validated object is null".</p>
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @param index the index to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated array (never {@code null} for method chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(Object[], int)
- *
- * @since 3.0
- */
- public static <T> T[] validIndex(T[] array, int index, String message, Object... values) {
- Validate.notNull(array);
- if (index < 0 || index >= array.length) {
- throw new IndexOutOfBoundsException(String.format(message, values));
- }
- return array;
- }
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * array; otherwise throwing an exception.</p>
- *
- * <pre>Validate.validIndex(myArray, 2);</pre>
- *
- * <p>If the array is {@code null}, then the message of the exception
- * is "The validated object is null".</p>
- *
- * <p>If the index is invalid, then the message of the exception is
- * "The validated array index is invalid: " followed by the
- * index.</p>
- *
- * @param <T> the array type
- * @param array the array to check, validated not null by this method
- * @param index the index to check
- * @return the validated array (never {@code null} for method chaining)
- * @throws NullPointerException if the array is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(Object[], int, String, Object...)
- *
- * @since 3.0
- */
- public static <T> T[] validIndex(T[] array, int index) {
- return validIndex(array, index, DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE, Integer.valueOf(index));
- }
-
- // validIndex collection
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * collection; otherwise throwing an exception with the specified message.</p>
- *
- * <pre>Validate.validIndex(myCollection, 2, "The collection index is invalid: ");</pre>
- *
- * <p>If the collection is {@code null}, then the message of the
- * exception is "The validated object is null".</p>
- *
- * @param <T> the collection type
- * @param collection the collection to check, validated not null by this method
- * @param index the index to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated collection (never {@code null} for chaining)
- * @throws NullPointerException if the collection is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(Collection, int)
- *
- * @since 3.0
- */
- public static <T extends Collection<?>> T validIndex(T collection, int index, String message, Object... values) {
- Validate.notNull(collection);
- if (index < 0 || index >= collection.size()) {
- throw new IndexOutOfBoundsException(String.format(message, values));
- }
- return collection;
- }
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * collection; otherwise throwing an exception.</p>
- *
- * <pre>Validate.validIndex(myCollection, 2);</pre>
- *
- * <p>If the index is invalid, then the message of the exception
- * is "The validated collection index is invalid: "
- * followed by the index.</p>
- *
- * @param <T> the collection type
- * @param collection the collection to check, validated not null by this method
- * @param index the index to check
- * @return the validated collection (never {@code null} for method chaining)
- * @throws NullPointerException if the collection is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(Collection, int, String, Object...)
- *
- * @since 3.0
- */
- public static <T extends Collection<?>> T validIndex(T collection, int index) {
- return validIndex(collection, index, DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE, Integer.valueOf(index));
- }
-
- // validIndex string
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * character sequence; otherwise throwing an exception with the
- * specified message.</p>
- *
- * <pre>Validate.validIndex(myStr, 2, "The string index is invalid: ");</pre>
- *
- * <p>If the character sequence is {@code null}, then the message
- * of the exception is "The validated object is null".</p>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @param index the index to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @return the validated character sequence (never {@code null} for method chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(CharSequence, int)
- *
- * @since 3.0
- */
- public static <T extends CharSequence> T validIndex(T chars, int index, String message, Object... values) {
- Validate.notNull(chars);
- if (index < 0 || index >= chars.length()) {
- throw new IndexOutOfBoundsException(String.format(message, values));
- }
- return chars;
- }
-
- /**
- * <p>Validates that the index is within the bounds of the argument
- * character sequence; otherwise throwing an exception.</p>
- *
- * <pre>Validate.validIndex(myStr, 2);</pre>
- *
- * <p>If the character sequence is {@code null}, then the message
- * of the exception is "The validated object is
- * null".</p>
- *
- * <p>If the index is invalid, then the message of the exception
- * is "The validated character sequence index is invalid: "
- * followed by the index.</p>
- *
- * @param <T> the character sequence type
- * @param chars the character sequence to check, validated not null by this method
- * @param index the index to check
- * @return the validated character sequence (never {@code null} for method chaining)
- * @throws NullPointerException if the character sequence is {@code null}
- * @throws IndexOutOfBoundsException if the index is invalid
- * @see #validIndex(CharSequence, int, String, Object...)
- *
- * @since 3.0
- */
- public static <T extends CharSequence> T validIndex(T chars, int index) {
- return validIndex(chars, index, DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE, Integer.valueOf(index));
- }
-
- // validState
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the stateful condition is {@code true}; otherwise
- * throwing an exception. This method is useful when validating according
- * to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>
- * Validate.validState(field > 0);
- * Validate.validState(this.isOk());</pre>
- *
- * <p>The message of the exception is "The validated state is
- * false".</p>
- *
- * @param expression the boolean expression to check
- * @throws IllegalStateException if expression is {@code false}
- * @see #validState(boolean, String, Object...)
- *
- * @since 3.0
- */
- public static void validState(boolean expression) {
- if (expression == false) {
- throw new IllegalStateException(DEFAULT_VALID_STATE_EX_MESSAGE);
- }
- }
-
- /**
- * <p>Validate that the stateful condition is {@code true}; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating according to an arbitrary boolean expression, such as validating a
- * primitive number or using your own custom validation expression.</p>
- *
- * <pre>Validate.validState(this.isOk(), "The state is not OK: %s", myObject);</pre>
- *
- * @param expression the boolean expression to check
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalStateException if expression is {@code false}
- * @see #validState(boolean)
- *
- * @since 3.0
- */
- public static void validState(boolean expression, String message, Object... values) {
- if (expression == false) {
- throw new IllegalStateException(String.format(message, values));
- }
- }
-
- // matchesPattern
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument character sequence matches the specified regular
- * expression pattern; otherwise throwing an exception.</p>
- *
- * <pre>Validate.matchesPattern("hi", "[a-z]*");</pre>
- *
- * <p>The syntax of the pattern is the one used in the {@link Pattern} class.</p>
- *
- * @param input the character sequence to validate, not null
- * @param pattern the regular expression pattern, not null
- * @throws IllegalArgumentException if the character sequence does not match the pattern
- * @see #matchesPattern(CharSequence, String, String, Object...)
- *
- * @since 3.0
- */
- public static void matchesPattern(CharSequence input, String pattern) {
- if (Pattern.matches(pattern, input) == false) {
- throw new IllegalArgumentException(String.format(DEFAULT_MATCHES_PATTERN_EX, input, pattern));
- }
- }
-
- /**
- * <p>Validate that the specified argument character sequence matches the specified regular
- * expression pattern; otherwise throwing an exception with the specified message.</p>
- *
- * <pre>Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");</pre>
- *
- * <p>The syntax of the pattern is the one used in the {@link Pattern} class.</p>
- *
- * @param input the character sequence to validate, not null
- * @param pattern the regular expression pattern, not null
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if the character sequence does not match the pattern
- * @see #matchesPattern(CharSequence, String)
- *
- * @since 3.0
- */
- public static void matchesPattern(CharSequence input, String pattern, String message, Object... values) {
- if (Pattern.matches(pattern, input) == false) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
- // inclusiveBetween
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument object fall between the two
- * inclusive values specified; otherwise, throws an exception.</p>
- *
- * <pre>Validate.inclusiveBetween(0, 2, 1);</pre>
- *
- * @param <T> the type of the argument object
- * @param start the inclusive start value, not null
- * @param end the inclusive end value, not null
- * @param value the object to validate, not null
- * @throws IllegalArgumentException if the value falls out of the boundaries
- * @see #inclusiveBetween(Object, Object, Comparable, String, Object...)
- *
- * @since 3.0
- */
- public static <T> void inclusiveBetween(T start, T end, Comparable<T> value) {
- if (value.compareTo(start) < 0 || value.compareTo(end) > 0) {
- throw new IllegalArgumentException(String.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end));
- }
- }
-
- /**
- * <p>Validate that the specified argument object fall between the two
- * inclusive values specified; otherwise, throws an exception with the
- * specified message.</p>
- *
- * <pre>Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");</pre>
- *
- * @param <T> the type of the argument object
- * @param start the inclusive start value, not null
- * @param end the inclusive end value, not null
- * @param value the object to validate, not null
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if the value falls out of the boundaries
- * @see #inclusiveBetween(Object, Object, Comparable)
- *
- * @since 3.0
- */
- public static <T> void inclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values) {
- if (value.compareTo(start) < 0 || value.compareTo(end) > 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
- // exclusiveBetween
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the specified argument object fall between the two
- * exclusive values specified; otherwise, throws an exception.</p>
- *
- * <pre>Validate.inclusiveBetween(0, 2, 1);</pre>
- *
- * @param <T> the type of the argument object
- * @param start the exclusive start value, not null
- * @param end the exclusive end value, not null
- * @param value the object to validate, not null
- * @throws IllegalArgumentException if the value falls out of the boundaries
- * @see #exclusiveBetween(Object, Object, Comparable, String, Object...)
- *
- * @since 3.0
- */
- public static <T> void exclusiveBetween(T start, T end, Comparable<T> value) {
- if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) {
- throw new IllegalArgumentException(String.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end));
- }
- }
-
- /**
- * <p>Validate that the specified argument object fall between the two
- * exclusive values specified; otherwise, throws an exception with the
- * specified message.</p>
- *
- * <pre>Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");</pre>
- *
- * @param <T> the type of the argument object
- * @param start the exclusive start value, not null
- * @param end the exclusive end value, not null
- * @param value the object to validate, not null
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if the value falls out of the boundaries
- * @see #exclusiveBetween(Object, Object, Comparable)
- *
- * @since 3.0
- */
- public static <T> void exclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values) {
- if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
- // isInstanceOf
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the argument is an instance of the specified class; otherwise
- * throwing an exception. This method is useful when validating according to an arbitrary
- * class</p>
- *
- * <pre>Validate.isInstanceOf(OkClass.class, object);</pre>
- *
- * <p>The message of the exception is "The validated object is not an instance of"
- * followed by the name of the class</p>
- *
- * @param type the class the object must be validated against, not null
- * @param obj the object to check, null throws an exception
- * @throws IllegalArgumentException if argument is not of specified class
- * @see #isInstanceOf(Class, Object, String, Object...)
- *
- * @since 3.0
- */
- public static void isInstanceOf(Class<?> type, Object obj) {
- if (type.isInstance(obj) == false) {
- throw new IllegalArgumentException(String.format(DEFAULT_IS_INSTANCE_OF_EX_MESSAGE, type.getName()));
- }
- }
-
- /**
- * <p>Validate that the argument is an instance of the specified class; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating according to an arbitrary class</p>
- *
- * <pre>Validate.isInstanceOf(OkClass.classs, object, "Wrong class, object is of class %s",
- * object.getClass().getName());</pre>
- *
- * @param type the class the object must be validated against, not null
- * @param obj the object to check, null throws an exception
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if argument is not of specified class
- * @see #isInstanceOf(Class, Object)
- *
- * @since 3.0
- */
- public static void isInstanceOf(Class<?> type, Object obj, String message, Object... values) {
- if (type.isInstance(obj) == false) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-
- // isAssignableFrom
- //---------------------------------------------------------------------------------
-
- /**
- * <p>Validate that the argument can be converted to the specified class; otherwise
- * throwing an exception with the specified message. This method is useful when
- * validating if there will be no casting errors.</p>
- *
- * <pre>Validate.isAssignableFrom(SuperClass.class, object.getClass());</pre>
- *
- * <p>The message of the exception is "The validated object can not be converted to the"
- * followed by the name of the class and "class"</p>
- *
- * @param superType the class the class must be validated against, not null
- * @param type the class to check, not null
- * @throws IllegalArgumentException if argument can not be converted to the specified class
- * @see #isAssignableFrom(Class, Class, String, Object...)
- *
- * @since 3.0
- */
- public static void isAssignableFrom(Class<?> superType, Class<?> type) {
- if (superType.isAssignableFrom(type) == false) {
- throw new IllegalArgumentException(String.format(DEFAULT_IS_ASSIGNABLE_EX_MESSAGE, superType.getName()));
- }
- }
-
- /**
- * <p>Validate that the argument can be converted to the specified class; otherwise
- * throwing an exception. This method is useful when validating if there will be no
- * casting errors.</p>
- *
- * <pre>Validate.isAssignableFrom(SuperClass.class, object.getClass());</pre>
- *
- * <p>The message of the exception is "The validated object can not be converted to the"
- * followed by the name of the class and "class"</p>
- *
- * @param superType the class the class must be validated against, not null
- * @param type the class to check, not null
- * @param message the {@link String#format(String, Object...)} exception message if invalid, not null
- * @param values the optional values for the formatted exception message, null array not recommended
- * @throws IllegalArgumentException if argument can not be converted to the specified class
- * @see #isAssignableFrom(Class, Class)
- */
- public static void isAssignableFrom(Class<?> superType, Class<?> type, String message, Object... values) {
- if (superType.isAssignableFrom(type) == false) {
- throw new IllegalArgumentException(String.format(message, values));
- }
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * <p>This class assists in validating arguments. The validation methods are + * based along the following principles: + * <ul> + * <li>An invalid {@code null} argument causes a {@link NullPointerException}.</li> + * <li>A non-{@code null} argument causes an {@link IllegalArgumentException}.</li> + * <li>An invalid index into an array/collection/map/string causes an {@link IndexOutOfBoundsException}.</li> + * </ul> + * + * <p>All exceptions messages are + * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/util/Formatter.html#syntax">format strings</a> + * as defined by the Java platform. For example:</p> + * + * <pre> + * Validate.isTrue(i > 0, "The value must be greater than zero: %d", i); + * Validate.notNull(surname, "The surname must not be %s", null); + * </pre> + * + * <p>#ThreadSafe#</p> + * @version $Id: Validate.java 1153490 2011-08-03 13:53:35Z ggregory $ + * @see java.lang.String#format(String, Object...) + * @since 2.0 + */ +public class Validate { + + private static final String DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE = + "The value %s is not in the specified exclusive range of %s to %s"; + private static final String DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE = + "The value %s is not in the specified inclusive range of %s to %s"; + private static final String DEFAULT_MATCHES_PATTERN_EX = "The string %s does not match the pattern %s"; + private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null"; + private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false"; + private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE = + "The validated array contains null element at index: %d"; + private static final String DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE = + "The validated collection contains null element at index: %d"; + private static final String DEFAULT_NOT_BLANK_EX_MESSAGE = "The validated character sequence is blank"; + private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty"; + private static final String DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE = + "The validated character sequence is empty"; + private static final String DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE = "The validated collection is empty"; + private static final String DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE = "The validated map is empty"; + private static final String DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE = "The validated array index is invalid: %d"; + private static final String DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE = + "The validated character sequence index is invalid: %d"; + private static final String DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE = + "The validated collection index is invalid: %d"; + private static final String DEFAULT_VALID_STATE_EX_MESSAGE = "The validated state is false"; + private static final String DEFAULT_IS_ASSIGNABLE_EX_MESSAGE = + "The validated class can not be converted to the %s class"; + private static final String DEFAULT_IS_INSTANCE_OF_EX_MESSAGE = "The validated object is not an instance of %s"; + + /** + * Constructor. This class should not normally be instantiated. + */ + public Validate() { + super(); + } + + // isTrue + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre>Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);</pre> + * + * <p>For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.</p> + * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression, String message, long value) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, Long.valueOf(value))); + } + } + + /** + * <p>Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre>Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);</pre> + * + * <p>For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.</p> + * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression, String message, double value) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, Double.valueOf(value))); + } + } + + /** + * <p>Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre> + * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max); + * Validate.isTrue(myObject.isOk(), "The object is not okay");</pre> + * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + */ + public static void isTrue(boolean expression, String message, Object... values) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + /** + * <p>Validate that the argument condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre> + * Validate.isTrue(i > 0); + * Validate.isTrue(myObject.isOk());</pre> + * + * <p>The message of the exception is "The validated expression is + * false".</p> + * + * @param expression the boolean expression to check + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression) { + if (expression == false) { + throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE); + } + } + + // notNull + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument is not {@code null}; + * otherwise throwing an exception. + * + * <pre>Validate.notNull(myObject, "The object must not be null");</pre> + * + * <p>The message of the exception is "The validated object is + * null".</p> + * + * @param <T> the object type + * @param object the object to check + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object, String, Object...) + */ + public static <T> T notNull(T object) { + return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE); + } + + /** + * <p>Validate that the specified argument is not {@code null}; + * otherwise throwing an exception with the specified message. + * + * <pre>Validate.notNull(myObject, "The object must not be null");</pre> + * + * @param <T> the object type + * @param object the object to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object) + */ + public static <T> T notNull(T object, String message, Object... values) { + if (object == null) { + throw new NullPointerException(String.format(message, values)); + } + return object; + } + + // notEmpty array + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument array is neither {@code null} + * nor a length of zero (no elements); otherwise throwing an exception + * with the specified message. + * + * <pre>Validate.notEmpty(myArray, "The array must not be empty");</pre> + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if the array is empty + * @see #notEmpty(Object[]) + */ + public static <T> T[] notEmpty(T[] array, String message, Object... values) { + if (array == null) { + throw new NullPointerException(String.format(message, values)); + } + if (array.length == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return array; + } + + /** + * <p>Validate that the specified argument array is neither {@code null} + * nor a length of zero (no elements); otherwise throwing an exception. + * + * <pre>Validate.notEmpty(myArray);</pre> + * + * <p>The message in the exception is "The validated array is + * empty". + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if the array is empty + * @see #notEmpty(Object[], String, Object...) + */ + public static <T> T[] notEmpty(T[] array) { + return notEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE); + } + + // notEmpty collection + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument collection is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + * <pre>Validate.notEmpty(myCollection, "The collection must not be empty");</pre> + * + * @param <T> the collection type + * @param collection the collection to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated collection (never {@code null} method for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IllegalArgumentException if the collection is empty + * @see #notEmpty(Object[]) + */ + public static <T extends Collection<?>> T notEmpty(T collection, String message, Object... values) { + if (collection == null) { + throw new NullPointerException(String.format(message, values)); + } + if (collection.size() == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return collection; + } + + /** + * <p>Validate that the specified argument collection is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception. + * + * <pre>Validate.notEmpty(myCollection);</pre> + * + * <p>The message in the exception is "The validated collection is + * empty".</p> + * + * @param <T> the collection type + * @param collection the collection to check, validated not null by this method + * @return the validated collection (never {@code null} method for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IllegalArgumentException if the collection is empty + * @see #notEmpty(Collection, String, Object...) + */ + public static <T extends Collection<?>> T notEmpty(T collection) { + return notEmpty(collection, DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE); + } + + // notEmpty map + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument map is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + * <pre>Validate.notEmpty(myMap, "The map must not be empty");</pre> + * + * @param <T> the map type + * @param map the map to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated map (never {@code null} method for chaining) + * @throws NullPointerException if the map is {@code null} + * @throws IllegalArgumentException if the map is empty + * @see #notEmpty(Object[]) + */ + public static <T extends Map<?, ?>> T notEmpty(T map, String message, Object... values) { + if (map == null) { + throw new NullPointerException(String.format(message, values)); + } + if (map.size() == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return map; + } + + /** + * <p>Validate that the specified argument map is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception. + * + * <pre>Validate.notEmpty(myMap);</pre> + * + * <p>The message in the exception is "The validated map is + * empty".</p> + * + * @param <T> the map type + * @param map the map to check, validated not null by this method + * @return the validated map (never {@code null} method for chaining) + * @throws NullPointerException if the map is {@code null} + * @throws IllegalArgumentException if the map is empty + * @see #notEmpty(Map, String, Object...) + */ + public static <T extends Map<?, ?>> T notEmpty(T map) { + return notEmpty(map, DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE); + } + + // notEmpty string + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + * + * <pre>Validate.notEmpty(myString, "The string must not be empty");</pre> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is empty + * @see #notEmpty(CharSequence) + */ + public static <T extends CharSequence> T notEmpty(T chars, String message, Object... values) { + if (chars == null) { + throw new NullPointerException(String.format(message, values)); + } + if (chars.length() == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return chars; + } + + /** + * <p>Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + * + * <pre>Validate.notEmpty(myString);</pre> + * + * <p>The message in the exception is "The validated + * character sequence is empty".</p> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is empty + * @see #notEmpty(CharSequence, String, Object...) + */ + public static <T extends CharSequence> T notEmpty(T chars) { + return notEmpty(chars, DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE); + } + + // notBlank string + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument character sequence is + * neither {@code null}, a length of zero (no characters), empty + * nor whitespace; otherwise throwing an exception with the specified + * message. + * + * <pre>Validate.notBlank(myString, "The string must not be blank");</pre> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is blank + * @see #notBlank(CharSequence) + * + * @since 3.0 + */ + public static <T extends CharSequence> T notBlank(T chars, String message, Object... values) { + if (chars == null) { + throw new NullPointerException(String.format(message, values)); + } + if (StringUtils.isBlank(chars)) { + throw new IllegalArgumentException(String.format(message, values)); + } + return chars; + } + + /** + * <p>Validate that the specified argument character sequence is + * neither {@code null}, a length of zero (no characters), empty + * nor whitespace; otherwise throwing an exception. + * + * <pre>Validate.notBlank(myString);</pre> + * + * <p>The message in the exception is "The validated character + * sequence is blank".</p> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is blank + * @see #notBlank(CharSequence, String, Object...) + * + * @since 3.0 + */ + public static <T extends CharSequence> T notBlank(T chars) { + return notBlank(chars, DEFAULT_NOT_BLANK_EX_MESSAGE); + } + + // noNullElements array + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument array is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + * <pre>Validate.noNullElements(myArray, "The array contain null at position %d");</pre> + * + * <p>If the array is {@code null}, then the message in the exception + * is "The validated object is null".</p> + * + * <p>If the array has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.</p> + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Object[]) + */ + public static <T> T[] noNullElements(T[] array, String message, Object... values) { + Validate.notNull(array); + for (int i = 0; i < array.length; i++) { + if (array[i] == null) { + Object[] values2 = ArrayUtils.add(values, Integer.valueOf(i)); + throw new IllegalArgumentException(String.format(message, values2)); + } + } + return array; + } + + /** + * <p>Validate that the specified argument array is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception. + * + * <pre>Validate.noNullElements(myArray);</pre> + * + * <p>If the array is {@code null}, then the message in the exception + * is "The validated object is null".</p> + * + * <p>If the array has a {@code null} element, then the message in the + * exception is "The validated array contains null element at index: + * " followed by the index.</p> + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Object[], String, Object...) + */ + public static <T> T[] noNullElements(T[] array) { + return noNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE); + } + + // noNullElements iterable + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument iterable is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + * <pre>Validate.noNullElements(myCollection, "The collection contains null at position %d");</pre> + * + * <p>If the iterable is {@code null}, then the message in the exception + * is "The validated object is null".</p> + * + * <p>If the iterable has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.</p> + * + * @param <T> the iterable type + * @param iterable the iterable to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated iterable (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Iterable) + */ + public static <T extends Iterable<?>> T noNullElements(T iterable, String message, Object... values) { + Validate.notNull(iterable); + int i = 0; + for (Iterator<?> it = iterable.iterator(); it.hasNext(); i++) { + if (it.next() == null) { + Object[] values2 = ArrayUtils.addAll(values, Integer.valueOf(i)); + throw new IllegalArgumentException(String.format(message, values2)); + } + } + return iterable; + } + + /** + * <p>Validate that the specified argument iterable is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception. + * + * <pre>Validate.noNullElements(myCollection);</pre> + * + * <p>If the iterable is {@code null}, then the message in the exception + * is "The validated object is null".</p> + * + * <p>If the array has a {@code null} element, then the message in the + * exception is "The validated iterable contains null element at index: + * " followed by the index.</p> + * + * @param <T> the iterable type + * @param iterable the iterable to check, validated not null by this method + * @return the validated iterable (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Iterable, String, Object...) + */ + public static <T extends Iterable<?>> T noNullElements(T iterable) { + return noNullElements(iterable, DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE); + } + + // validIndex array + //--------------------------------------------------------------------------------- + + /** + * <p>Validates that the index is within the bounds of the argument + * array; otherwise throwing an exception with the specified message.</p> + * + * <pre>Validate.validIndex(myArray, 2, "The array index is invalid: ");</pre> + * + * <p>If the array is {@code null}, then the message of the exception + * is "The validated object is null".</p> + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} for method chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Object[], int) + * + * @since 3.0 + */ + public static <T> T[] validIndex(T[] array, int index, String message, Object... values) { + Validate.notNull(array); + if (index < 0 || index >= array.length) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return array; + } + + /** + * <p>Validates that the index is within the bounds of the argument + * array; otherwise throwing an exception.</p> + * + * <pre>Validate.validIndex(myArray, 2);</pre> + * + * <p>If the array is {@code null}, then the message of the exception + * is "The validated object is null".</p> + * + * <p>If the index is invalid, then the message of the exception is + * "The validated array index is invalid: " followed by the + * index.</p> + * + * @param <T> the array type + * @param array the array to check, validated not null by this method + * @param index the index to check + * @return the validated array (never {@code null} for method chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Object[], int, String, Object...) + * + * @since 3.0 + */ + public static <T> T[] validIndex(T[] array, int index) { + return validIndex(array, index, DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE, Integer.valueOf(index)); + } + + // validIndex collection + //--------------------------------------------------------------------------------- + + /** + * <p>Validates that the index is within the bounds of the argument + * collection; otherwise throwing an exception with the specified message.</p> + * + * <pre>Validate.validIndex(myCollection, 2, "The collection index is invalid: ");</pre> + * + * <p>If the collection is {@code null}, then the message of the + * exception is "The validated object is null".</p> + * + * @param <T> the collection type + * @param collection the collection to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated collection (never {@code null} for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Collection, int) + * + * @since 3.0 + */ + public static <T extends Collection<?>> T validIndex(T collection, int index, String message, Object... values) { + Validate.notNull(collection); + if (index < 0 || index >= collection.size()) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return collection; + } + + /** + * <p>Validates that the index is within the bounds of the argument + * collection; otherwise throwing an exception.</p> + * + * <pre>Validate.validIndex(myCollection, 2);</pre> + * + * <p>If the index is invalid, then the message of the exception + * is "The validated collection index is invalid: " + * followed by the index.</p> + * + * @param <T> the collection type + * @param collection the collection to check, validated not null by this method + * @param index the index to check + * @return the validated collection (never {@code null} for method chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Collection, int, String, Object...) + * + * @since 3.0 + */ + public static <T extends Collection<?>> T validIndex(T collection, int index) { + return validIndex(collection, index, DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE, Integer.valueOf(index)); + } + + // validIndex string + //--------------------------------------------------------------------------------- + + /** + * <p>Validates that the index is within the bounds of the argument + * character sequence; otherwise throwing an exception with the + * specified message.</p> + * + * <pre>Validate.validIndex(myStr, 2, "The string index is invalid: ");</pre> + * + * <p>If the character sequence is {@code null}, then the message + * of the exception is "The validated object is null".</p> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} for method chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(CharSequence, int) + * + * @since 3.0 + */ + public static <T extends CharSequence> T validIndex(T chars, int index, String message, Object... values) { + Validate.notNull(chars); + if (index < 0 || index >= chars.length()) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return chars; + } + + /** + * <p>Validates that the index is within the bounds of the argument + * character sequence; otherwise throwing an exception.</p> + * + * <pre>Validate.validIndex(myStr, 2);</pre> + * + * <p>If the character sequence is {@code null}, then the message + * of the exception is "The validated object is + * null".</p> + * + * <p>If the index is invalid, then the message of the exception + * is "The validated character sequence index is invalid: " + * followed by the index.</p> + * + * @param <T> the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param index the index to check + * @return the validated character sequence (never {@code null} for method chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(CharSequence, int, String, Object...) + * + * @since 3.0 + */ + public static <T extends CharSequence> T validIndex(T chars, int index) { + return validIndex(chars, index, DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE, Integer.valueOf(index)); + } + + // validState + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the stateful condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre> + * Validate.validState(field > 0); + * Validate.validState(this.isOk());</pre> + * + * <p>The message of the exception is "The validated state is + * false".</p> + * + * @param expression the boolean expression to check + * @throws IllegalStateException if expression is {@code false} + * @see #validState(boolean, String, Object...) + * + * @since 3.0 + */ + public static void validState(boolean expression) { + if (expression == false) { + throw new IllegalStateException(DEFAULT_VALID_STATE_EX_MESSAGE); + } + } + + /** + * <p>Validate that the stateful condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.</p> + * + * <pre>Validate.validState(this.isOk(), "The state is not OK: %s", myObject);</pre> + * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalStateException if expression is {@code false} + * @see #validState(boolean) + * + * @since 3.0 + */ + public static void validState(boolean expression, String message, Object... values) { + if (expression == false) { + throw new IllegalStateException(String.format(message, values)); + } + } + + // matchesPattern + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument character sequence matches the specified regular + * expression pattern; otherwise throwing an exception.</p> + * + * <pre>Validate.matchesPattern("hi", "[a-z]*");</pre> + * + * <p>The syntax of the pattern is the one used in the {@link Pattern} class.</p> + * + * @param input the character sequence to validate, not null + * @param pattern the regular expression pattern, not null + * @throws IllegalArgumentException if the character sequence does not match the pattern + * @see #matchesPattern(CharSequence, String, String, Object...) + * + * @since 3.0 + */ + public static void matchesPattern(CharSequence input, String pattern) { + if (Pattern.matches(pattern, input) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_MATCHES_PATTERN_EX, input, pattern)); + } + } + + /** + * <p>Validate that the specified argument character sequence matches the specified regular + * expression pattern; otherwise throwing an exception with the specified message.</p> + * + * <pre>Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");</pre> + * + * <p>The syntax of the pattern is the one used in the {@link Pattern} class.</p> + * + * @param input the character sequence to validate, not null + * @param pattern the regular expression pattern, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the character sequence does not match the pattern + * @see #matchesPattern(CharSequence, String) + * + * @since 3.0 + */ + public static void matchesPattern(CharSequence input, String pattern, String message, Object... values) { + if (Pattern.matches(pattern, input) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // inclusiveBetween + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument object fall between the two + * inclusive values specified; otherwise, throws an exception.</p> + * + * <pre>Validate.inclusiveBetween(0, 2, 1);</pre> + * + * @param <T> the type of the argument object + * @param start the inclusive start value, not null + * @param end the inclusive end value, not null + * @param value the object to validate, not null + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #inclusiveBetween(Object, Object, Comparable, String, Object...) + * + * @since 3.0 + */ + public static <T> void inclusiveBetween(T start, T end, Comparable<T> value) { + if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { + throw new IllegalArgumentException(String.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); + } + } + + /** + * <p>Validate that the specified argument object fall between the two + * inclusive values specified; otherwise, throws an exception with the + * specified message.</p> + * + * <pre>Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");</pre> + * + * @param <T> the type of the argument object + * @param start the inclusive start value, not null + * @param end the inclusive end value, not null + * @param value the object to validate, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #inclusiveBetween(Object, Object, Comparable) + * + * @since 3.0 + */ + public static <T> void inclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values) { + if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // exclusiveBetween + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the specified argument object fall between the two + * exclusive values specified; otherwise, throws an exception.</p> + * + * <pre>Validate.inclusiveBetween(0, 2, 1);</pre> + * + * @param <T> the type of the argument object + * @param start the exclusive start value, not null + * @param end the exclusive end value, not null + * @param value the object to validate, not null + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #exclusiveBetween(Object, Object, Comparable, String, Object...) + * + * @since 3.0 + */ + public static <T> void exclusiveBetween(T start, T end, Comparable<T> value) { + if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { + throw new IllegalArgumentException(String.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); + } + } + + /** + * <p>Validate that the specified argument object fall between the two + * exclusive values specified; otherwise, throws an exception with the + * specified message.</p> + * + * <pre>Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");</pre> + * + * @param <T> the type of the argument object + * @param start the exclusive start value, not null + * @param end the exclusive end value, not null + * @param value the object to validate, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #exclusiveBetween(Object, Object, Comparable) + * + * @since 3.0 + */ + public static <T> void exclusiveBetween(T start, T end, Comparable<T> value, String message, Object... values) { + if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // isInstanceOf + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the argument is an instance of the specified class; otherwise + * throwing an exception. This method is useful when validating according to an arbitrary + * class</p> + * + * <pre>Validate.isInstanceOf(OkClass.class, object);</pre> + * + * <p>The message of the exception is "The validated object is not an instance of" + * followed by the name of the class</p> + * + * @param type the class the object must be validated against, not null + * @param obj the object to check, null throws an exception + * @throws IllegalArgumentException if argument is not of specified class + * @see #isInstanceOf(Class, Object, String, Object...) + * + * @since 3.0 + */ + public static void isInstanceOf(Class<?> type, Object obj) { + if (type.isInstance(obj) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_IS_INSTANCE_OF_EX_MESSAGE, type.getName())); + } + } + + /** + * <p>Validate that the argument is an instance of the specified class; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary class</p> + * + * <pre>Validate.isInstanceOf(OkClass.classs, object, "Wrong class, object is of class %s", + * object.getClass().getName());</pre> + * + * @param type the class the object must be validated against, not null + * @param obj the object to check, null throws an exception + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument is not of specified class + * @see #isInstanceOf(Class, Object) + * + * @since 3.0 + */ + public static void isInstanceOf(Class<?> type, Object obj, String message, Object... values) { + if (type.isInstance(obj) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // isAssignableFrom + //--------------------------------------------------------------------------------- + + /** + * <p>Validate that the argument can be converted to the specified class; otherwise + * throwing an exception with the specified message. This method is useful when + * validating if there will be no casting errors.</p> + * + * <pre>Validate.isAssignableFrom(SuperClass.class, object.getClass());</pre> + * + * <p>The message of the exception is "The validated object can not be converted to the" + * followed by the name of the class and "class"</p> + * + * @param superType the class the class must be validated against, not null + * @param type the class to check, not null + * @throws IllegalArgumentException if argument can not be converted to the specified class + * @see #isAssignableFrom(Class, Class, String, Object...) + * + * @since 3.0 + */ + public static void isAssignableFrom(Class<?> superType, Class<?> type) { + if (superType.isAssignableFrom(type) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_IS_ASSIGNABLE_EX_MESSAGE, superType.getName())); + } + } + + /** + * <p>Validate that the argument can be converted to the specified class; otherwise + * throwing an exception. This method is useful when validating if there will be no + * casting errors.</p> + * + * <pre>Validate.isAssignableFrom(SuperClass.class, object.getClass());</pre> + * + * <p>The message of the exception is "The validated object can not be converted to the" + * followed by the name of the class and "class"</p> + * + * @param superType the class the class must be validated against, not null + * @param type the class to check, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument can not be converted to the specified class + * @see #isAssignableFrom(Class, Class) + */ + public static void isAssignableFrom(Class<?> superType, Class<?> type, String message, Object... values) { + if (superType.isAssignableFrom(type) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } +} diff --git a/src/org/apache/commons/lang3/builder/Builder.java b/src/org/apache/commons/lang3/builder/Builder.java index ce696e8..1d9037e 100644 --- a/src/org/apache/commons/lang3/builder/Builder.java +++ b/src/org/apache/commons/lang3/builder/Builder.java @@ -1,89 +1,89 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-/**
- * <p>
- * The Builder interface is designed to designate a class as a <em>builder</em>
- * object in the Builder design pattern. Builders are capable of creating and
- * configuring objects or results that normally take multiple steps to construct
- * or are very complex to derive.
- * </p>
- *
- * <p>
- * The builder interface defines a single method, {@link #build()}, that
- * classes must implement. The result of this method should be the final
- * configured object or result after all building operations are performed.
- * </p>
- *
- * <p>
- * It is a recommended practice that the methods supplied to configure the
- * object or result being built return a reference to {@code this} so that
- * method calls can be chained together.
- * </p>
- *
- * <p>
- * Example Builder:
- * <code><pre>
- * class FontBuilder implements Builder<Font> {
- * private Font font;
- *
- * public FontBuilder(String fontName) {
- * this.font = new Font(fontName, Font.PLAIN, 12);
- * }
- *
- * public FontBuilder bold() {
- * this.font = this.font.deriveFont(Font.BOLD);
- * return this; // Reference returned so calls can be chained
- * }
- *
- * public FontBuilder size(float pointSize) {
- * this.font = this.font.deriveFont(pointSize);
- * return this; // Reference returned so calls can be chained
- * }
- *
- * // Other Font construction methods
- *
- * public Font build() {
- * return this.font;
- * }
- * }
- * </pre></code>
- *
- * Example Builder Usage:
- * <code><pre>
- * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
- * .size(14.0f)
- * .build();
- * </pre></code>
- * </p>
- *
- * @param <T> the type of object that the builder will construct or compute.
- *
- * @since 3.0
- * @version $Id: Builder.java 1088899 2011-04-05 05:31:27Z bayard $
- */
-public interface Builder<T> {
-
- /**
- * Returns a reference to the object being constructed or result being
- * calculated by the builder.
- *
- * @return the object constructed or result calculated by the builder.
- */
- public T build();
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +/** + * <p> + * The Builder interface is designed to designate a class as a <em>builder</em> + * object in the Builder design pattern. Builders are capable of creating and + * configuring objects or results that normally take multiple steps to construct + * or are very complex to derive. + * </p> + * + * <p> + * The builder interface defines a single method, {@link #build()}, that + * classes must implement. The result of this method should be the final + * configured object or result after all building operations are performed. + * </p> + * + * <p> + * It is a recommended practice that the methods supplied to configure the + * object or result being built return a reference to {@code this} so that + * method calls can be chained together. + * </p> + * + * <p> + * Example Builder: + * <code><pre> + * class FontBuilder implements Builder<Font> { + * private Font font; + * + * public FontBuilder(String fontName) { + * this.font = new Font(fontName, Font.PLAIN, 12); + * } + * + * public FontBuilder bold() { + * this.font = this.font.deriveFont(Font.BOLD); + * return this; // Reference returned so calls can be chained + * } + * + * public FontBuilder size(float pointSize) { + * this.font = this.font.deriveFont(pointSize); + * return this; // Reference returned so calls can be chained + * } + * + * // Other Font construction methods + * + * public Font build() { + * return this.font; + * } + * } + * </pre></code> + * + * Example Builder Usage: + * <code><pre> + * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold() + * .size(14.0f) + * .build(); + * </pre></code> + * </p> + * + * @param <T> the type of object that the builder will construct or compute. + * + * @since 3.0 + * @version $Id: Builder.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public interface Builder<T> { + + /** + * Returns a reference to the object being constructed or result being + * calculated by the builder. + * + * @return the object constructed or result calculated by the builder. + */ + public T build(); +} diff --git a/src/org/apache/commons/lang3/builder/CompareToBuilder.java b/src/org/apache/commons/lang3/builder/CompareToBuilder.java index 6f51a2f..7d86d98 100644 --- a/src/org/apache/commons/lang3/builder/CompareToBuilder.java +++ b/src/org/apache/commons/lang3/builder/CompareToBuilder.java @@ -1,1019 +1,1019 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.Comparator;
-
-import org.apache.commons.lang3.ArrayUtils;
-
-/**
- * Assists in implementing {@link java.lang.Comparable#compareTo(Object)} methods.
- *
- * It is consistent with <code>equals(Object)</code> and
- * <code>hashcode()</code> built with {@link EqualsBuilder} and
- * {@link HashCodeBuilder}.</p>
- *
- * <p>Two Objects that compare equal using <code>equals(Object)</code> should normally
- * also compare equal using <code>compareTo(Object)</code>.</p>
- *
- * <p>All relevant fields should be included in the calculation of the
- * comparison. Derived fields may be ignored. The same fields, in the same
- * order, should be used in both <code>compareTo(Object)</code> and
- * <code>equals(Object)</code>.</p>
- *
- * <p>To use this class write code as follows:</p>
- *
- * <pre>
- * public class MyClass {
- * String field1;
- * int field2;
- * boolean field3;
- *
- * ...
- *
- * public int compareTo(Object o) {
- * MyClass myClass = (MyClass) o;
- * return new CompareToBuilder()
- * .appendSuper(super.compareTo(o)
- * .append(this.field1, myClass.field1)
- * .append(this.field2, myClass.field2)
- * .append(this.field3, myClass.field3)
- * .toComparison();
- * }
- * }
- * </pre>
- *
- * <p>Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use
- * reflection to determine the fields to append. Because fields can be private,
- * <code>reflectionCompare</code> uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to
- * bypass normal access control checks. This will fail under a security manager,
- * unless the appropriate permissions are set up correctly. It is also
- * slower than appending explicitly.</p>
- *
- * <p>A typical implementation of <code>compareTo(Object)</code> using
- * <code>reflectionCompare</code> looks like:</p>
-
- * <pre>
- * public int compareTo(Object o) {
- * return CompareToBuilder.reflectionCompare(this, o);
- * }
- * </pre>
- *
- * @see java.lang.Comparable
- * @see java.lang.Object#equals(Object)
- * @see java.lang.Object#hashCode()
- * @see EqualsBuilder
- * @see HashCodeBuilder
- * @since 1.0
- * @version $Id: CompareToBuilder.java 1090813 2011-04-10 15:03:23Z mbenson $
- */
-public class CompareToBuilder implements Builder<Integer> {
-
- /**
- * Current state of the comparison as appended fields are checked.
- */
- private int comparison;
-
- /**
- * <p>Constructor for CompareToBuilder.</p>
- *
- * <p>Starts off assuming that the objects are equal. Multiple calls are
- * then made to the various append methods, followed by a call to
- * {@link #toComparison} to get the result.</p>
- */
- public CompareToBuilder() {
- super();
- comparison = 0;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Compares two <code>Object</code>s via reflection.</p>
- *
- * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
- * is used to bypass normal access control checks. This will fail under a
- * security manager unless the appropriate permissions are set.</p>
- *
- * <ul>
- * <li>Static fields will not be compared</li>
- * <li>Transient members will be not be compared, as they are likely derived
- * fields</li>
- * <li>Superclass fields will be compared</li>
- * </ul>
- *
- * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
- * they are considered equal.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @return a negative integer, zero, or a positive integer as <code>lhs</code>
- * is less than, equal to, or greater than <code>rhs</code>
- * @throws NullPointerException if either (but not both) parameters are
- * <code>null</code>
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- */
- public static int reflectionCompare(Object lhs, Object rhs) {
- return reflectionCompare(lhs, rhs, false, null);
- }
-
- /**
- * <p>Compares two <code>Object</code>s via reflection.</p>
- *
- * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
- * is used to bypass normal access control checks. This will fail under a
- * security manager unless the appropriate permissions are set.</p>
- *
- * <ul>
- * <li>Static fields will not be compared</li>
- * <li>If <code>compareTransients</code> is <code>true</code>,
- * compares transient members. Otherwise ignores them, as they
- * are likely derived fields.</li>
- * <li>Superclass fields will be compared</li>
- * </ul>
- *
- * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
- * they are considered equal.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param compareTransients whether to compare transient fields
- * @return a negative integer, zero, or a positive integer as <code>lhs</code>
- * is less than, equal to, or greater than <code>rhs</code>
- * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
- * (but not both) is <code>null</code>
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- */
- public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) {
- return reflectionCompare(lhs, rhs, compareTransients, null);
- }
-
- /**
- * <p>Compares two <code>Object</code>s via reflection.</p>
- *
- * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
- * is used to bypass normal access control checks. This will fail under a
- * security manager unless the appropriate permissions are set.</p>
- *
- * <ul>
- * <li>Static fields will not be compared</li>
- * <li>If <code>compareTransients</code> is <code>true</code>,
- * compares transient members. Otherwise ignores them, as they
- * are likely derived fields.</li>
- * <li>Superclass fields will be compared</li>
- * </ul>
- *
- * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
- * they are considered equal.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param excludeFields Collection of String fields to exclude
- * @return a negative integer, zero, or a positive integer as <code>lhs</code>
- * is less than, equal to, or greater than <code>rhs</code>
- * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
- * (but not both) is <code>null</code>
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- * @since 2.2
- */
- public static int reflectionCompare(Object lhs, Object rhs, Collection<String> excludeFields) {
- return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
- }
-
- /**
- * <p>Compares two <code>Object</code>s via reflection.</p>
- *
- * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
- * is used to bypass normal access control checks. This will fail under a
- * security manager unless the appropriate permissions are set.</p>
- *
- * <ul>
- * <li>Static fields will not be compared</li>
- * <li>If <code>compareTransients</code> is <code>true</code>,
- * compares transient members. Otherwise ignores them, as they
- * are likely derived fields.</li>
- * <li>Superclass fields will be compared</li>
- * </ul>
- *
- * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
- * they are considered equal.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param excludeFields array of fields to exclude
- * @return a negative integer, zero, or a positive integer as <code>lhs</code>
- * is less than, equal to, or greater than <code>rhs</code>
- * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
- * (but not both) is <code>null</code>
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- * @since 2.2
- */
- public static int reflectionCompare(Object lhs, Object rhs, String... excludeFields) {
- return reflectionCompare(lhs, rhs, false, null, excludeFields);
- }
-
- /**
- * <p>Compares two <code>Object</code>s via reflection.</p>
- *
- * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code>
- * is used to bypass normal access control checks. This will fail under a
- * security manager unless the appropriate permissions are set.</p>
- *
- * <ul>
- * <li>Static fields will not be compared</li>
- * <li>If the <code>compareTransients</code> is <code>true</code>,
- * compares transient members. Otherwise ignores them, as they
- * are likely derived fields.</li>
- * <li>Compares superclass fields up to and including <code>reflectUpToClass</code>.
- * If <code>reflectUpToClass</code> is <code>null</code>, compares all superclass fields.</li>
- * </ul>
- *
- * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>,
- * they are considered equal.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param compareTransients whether to compare transient fields
- * @param reflectUpToClass last superclass for which fields are compared
- * @param excludeFields fields to exclude
- * @return a negative integer, zero, or a positive integer as <code>lhs</code>
- * is less than, equal to, or greater than <code>rhs</code>
- * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code>
- * (but not both) is <code>null</code>
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- * @since 2.2 (2.0 as <code>reflectionCompare(Object, Object, boolean, Class)</code>)
- */
- public static int reflectionCompare(
- Object lhs,
- Object rhs,
- boolean compareTransients,
- Class<?> reflectUpToClass,
- String... excludeFields) {
-
- if (lhs == rhs) {
- return 0;
- }
- if (lhs == null || rhs == null) {
- throw new NullPointerException();
- }
- Class<?> lhsClazz = lhs.getClass();
- if (!lhsClazz.isInstance(rhs)) {
- throw new ClassCastException();
- }
- CompareToBuilder compareToBuilder = new CompareToBuilder();
- reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
- while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) {
- lhsClazz = lhsClazz.getSuperclass();
- reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields);
- }
- return compareToBuilder.toComparison();
- }
-
- /**
- * <p>Appends to <code>builder</code> the comparison of <code>lhs</code>
- * to <code>rhs</code> using the fields defined in <code>clazz</code>.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param clazz <code>Class</code> that defines fields to be compared
- * @param builder <code>CompareToBuilder</code> to append to
- * @param useTransients whether to compare transient fields
- * @param excludeFields fields to exclude
- */
- private static void reflectionAppend(
- Object lhs,
- Object rhs,
- Class<?> clazz,
- CompareToBuilder builder,
- boolean useTransients,
- String[] excludeFields) {
-
- Field[] fields = clazz.getDeclaredFields();
- AccessibleObject.setAccessible(fields, true);
- for (int i = 0; i < fields.length && builder.comparison == 0; i++) {
- Field f = fields[i];
- if (!ArrayUtils.contains(excludeFields, f.getName())
- && (f.getName().indexOf('$') == -1)
- && (useTransients || !Modifier.isTransient(f.getModifiers()))
- && (!Modifier.isStatic(f.getModifiers()))) {
- try {
- builder.append(f.get(lhs), f.get(rhs));
- } catch (IllegalAccessException e) {
- // This can't happen. Would get a Security exception instead.
- // Throw a runtime exception in case the impossible happens.
- throw new InternalError("Unexpected IllegalAccessException");
- }
- }
- }
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Appends to the <code>builder</code> the <code>compareTo(Object)</code>
- * result of the superclass.</p>
- *
- * @param superCompareTo result of calling <code>super.compareTo(Object)</code>
- * @return this - used to chain append calls
- * @since 2.0
- */
- public CompareToBuilder appendSuper(int superCompareTo) {
- if (comparison != 0) {
- return this;
- }
- comparison = superCompareTo;
- return this;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Appends to the <code>builder</code> the comparison of
- * two <code>Object</code>s.</p>
- *
- * <ol>
- * <li>Check if <code>lhs == rhs</code></li>
- * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
- * a <code>null</code> object is less than a non-<code>null</code> object</li>
- * <li>Check the object contents</li>
- * </ol>
- *
- * <p><code>lhs</code> must either be an array or implement {@link Comparable}.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @return this - used to chain append calls
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- */
- public CompareToBuilder append(Object lhs, Object rhs) {
- return append(lhs, rhs, null);
- }
-
- /**
- * <p>Appends to the <code>builder</code> the comparison of
- * two <code>Object</code>s.</p>
- *
- * <ol>
- * <li>Check if <code>lhs == rhs</code></li>
- * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>,
- * a <code>null</code> object is less than a non-<code>null</code> object</li>
- * <li>Check the object contents</li>
- * </ol>
- *
- * <p>If <code>lhs</code> is an array, array comparison methods will be used.
- * Otherwise <code>comparator</code> will be used to compare the objects.
- * If <code>comparator</code> is <code>null</code>, <code>lhs</code> must
- * implement {@link Comparable} instead.</p>
- *
- * @param lhs left-hand object
- * @param rhs right-hand object
- * @param comparator <code>Comparator</code> used to compare the objects,
- * <code>null</code> means treat lhs as <code>Comparable</code>
- * @return this - used to chain append calls
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- * @since 2.0
- */
- public CompareToBuilder append(Object lhs, Object rhs, Comparator<?> comparator) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.getClass().isArray()) {
- // switch on type of array, to dispatch to the correct handler
- // handles multi dimensional arrays
- // throws a ClassCastException if rhs is not the correct array type
- if (lhs instanceof long[]) {
- append((long[]) lhs, (long[]) rhs);
- } else if (lhs instanceof int[]) {
- append((int[]) lhs, (int[]) rhs);
- } else if (lhs instanceof short[]) {
- append((short[]) lhs, (short[]) rhs);
- } else if (lhs instanceof char[]) {
- append((char[]) lhs, (char[]) rhs);
- } else if (lhs instanceof byte[]) {
- append((byte[]) lhs, (byte[]) rhs);
- } else if (lhs instanceof double[]) {
- append((double[]) lhs, (double[]) rhs);
- } else if (lhs instanceof float[]) {
- append((float[]) lhs, (float[]) rhs);
- } else if (lhs instanceof boolean[]) {
- append((boolean[]) lhs, (boolean[]) rhs);
- } else {
- // not an array of primitives
- // throws a ClassCastException if rhs is not an array
- append((Object[]) lhs, (Object[]) rhs, comparator);
- }
- } else {
- // the simple case, not an array, just test the element
- if (comparator == null) {
- @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
- final Comparable<Object> comparable = (Comparable<Object>) lhs;
- comparison = comparable.compareTo(rhs);
- } else {
- @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc
- final Comparator<Object> comparator2 = (Comparator<Object>) comparator;
- comparison = comparator2.compare(lhs, rhs);
- }
- }
- return this;
- }
-
- //-------------------------------------------------------------------------
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>long</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(long lhs, long rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
- return this;
- }
-
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>int</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(int lhs, int rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
- return this;
- }
-
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>short</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(short lhs, short rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
- return this;
- }
-
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>char</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(char lhs, char rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
- return this;
- }
-
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>byte</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(byte lhs, byte rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0));
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the comparison of
- * two <code>double</code>s.</p>
- *
- * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
- *
- * <p>It is compatible with the hash code generated by
- * <code>HashCodeBuilder</code>.</p>
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(double lhs, double rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = Double.compare(lhs, rhs);
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the comparison of
- * two <code>float</code>s.</p>
- *
- * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
- *
- * <p>It is compatible with the hash code generated by
- * <code>HashCodeBuilder</code>.</p>
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(float lhs, float rhs) {
- if (comparison != 0) {
- return this;
- }
- comparison = Float.compare(lhs, rhs);
- return this;
- }
-
- /**
- * Appends to the <code>builder</code> the comparison of
- * two <code>booleans</code>s.
- *
- * @param lhs left-hand value
- * @param rhs right-hand value
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(boolean lhs, boolean rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == false) {
- comparison = -1;
- } else {
- comparison = +1;
- }
- return this;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>Object</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a short length array is less than a long length array</li>
- * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
- * </ol>
- *
- * <p>This method will also will be called for the top level of multi-dimensional,
- * ragged, and multi-typed arrays.</p>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- */
- public CompareToBuilder append(Object[] lhs, Object[] rhs) {
- return append(lhs, rhs, null);
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>Object</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a short length array is less than a long length array</li>
- * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li>
- * </ol>
- *
- * <p>This method will also will be called for the top level of multi-dimensional,
- * ragged, and multi-typed arrays.</p>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @param comparator <code>Comparator</code> to use to compare the array elements,
- * <code>null</code> means to treat <code>lhs</code> elements as <code>Comparable</code>.
- * @return this - used to chain append calls
- * @throws ClassCastException if <code>rhs</code> is not assignment-compatible
- * with <code>lhs</code>
- * @since 2.0
- */
- public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator<?> comparator) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i], comparator);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>long</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(long, long)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(long[] lhs, long[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>int</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(int, int)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(int[] lhs, int[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>short</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(short, short)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(short[] lhs, short[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>char</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(char, char)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(char[] lhs, char[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>byte</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(byte, byte)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(byte[] lhs, byte[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>double</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(double, double)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(double[] lhs, double[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>float</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(float, float)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(float[] lhs, float[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Appends to the <code>builder</code> the deep comparison of
- * two <code>boolean</code> arrays.</p>
- *
- * <ol>
- * <li>Check if arrays are the same using <code>==</code></li>
- * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li>
- * <li>Check array length, a shorter length array is less than a longer length array</li>
- * <li>Check array contents element by element using {@link #append(boolean, boolean)}</li>
- * </ol>
- *
- * @param lhs left-hand array
- * @param rhs right-hand array
- * @return this - used to chain append calls
- */
- public CompareToBuilder append(boolean[] lhs, boolean[] rhs) {
- if (comparison != 0) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null) {
- comparison = -1;
- return this;
- }
- if (rhs == null) {
- comparison = +1;
- return this;
- }
- if (lhs.length != rhs.length) {
- comparison = (lhs.length < rhs.length) ? -1 : +1;
- return this;
- }
- for (int i = 0; i < lhs.length && comparison == 0; i++) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns a negative integer, a positive integer, or zero as
- * the <code>builder</code> has judged the "left-hand" side
- * as less than, greater than, or equal to the "right-hand"
- * side.
- *
- * @return final comparison result
- */
- public int toComparison() {
- return comparison;
- }
-
- /**
- * Returns a negative integer, a positive integer, or zero as
- * the <code>builder</code> has judged the "left-hand" side
- * as less than, greater than, or equal to the "right-hand"
- * side.
- *
- * @return final comparison result
- *
- * @since 3.0
- */
- public Integer build() {
- return Integer.valueOf(toComparison());
- }
-}
-
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Comparator; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Assists in implementing {@link java.lang.Comparable#compareTo(Object)} methods. + * + * It is consistent with <code>equals(Object)</code> and + * <code>hashcode()</code> built with {@link EqualsBuilder} and + * {@link HashCodeBuilder}.</p> + * + * <p>Two Objects that compare equal using <code>equals(Object)</code> should normally + * also compare equal using <code>compareTo(Object)</code>.</p> + * + * <p>All relevant fields should be included in the calculation of the + * comparison. Derived fields may be ignored. The same fields, in the same + * order, should be used in both <code>compareTo(Object)</code> and + * <code>equals(Object)</code>.</p> + * + * <p>To use this class write code as follows:</p> + * + * <pre> + * public class MyClass { + * String field1; + * int field2; + * boolean field3; + * + * ... + * + * public int compareTo(Object o) { + * MyClass myClass = (MyClass) o; + * return new CompareToBuilder() + * .appendSuper(super.compareTo(o) + * .append(this.field1, myClass.field1) + * .append(this.field2, myClass.field2) + * .append(this.field3, myClass.field3) + * .toComparison(); + * } + * } + * </pre> + * + * <p>Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use + * reflection to determine the fields to append. Because fields can be private, + * <code>reflectionCompare</code> uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to + * bypass normal access control checks. This will fail under a security manager, + * unless the appropriate permissions are set up correctly. It is also + * slower than appending explicitly.</p> + * + * <p>A typical implementation of <code>compareTo(Object)</code> using + * <code>reflectionCompare</code> looks like:</p> + + * <pre> + * public int compareTo(Object o) { + * return CompareToBuilder.reflectionCompare(this, o); + * } + * </pre> + * + * @see java.lang.Comparable + * @see java.lang.Object#equals(Object) + * @see java.lang.Object#hashCode() + * @see EqualsBuilder + * @see HashCodeBuilder + * @since 1.0 + * @version $Id: CompareToBuilder.java 1090813 2011-04-10 15:03:23Z mbenson $ + */ +public class CompareToBuilder implements Builder<Integer> { + + /** + * Current state of the comparison as appended fields are checked. + */ + private int comparison; + + /** + * <p>Constructor for CompareToBuilder.</p> + * + * <p>Starts off assuming that the objects are equal. Multiple calls are + * then made to the various append methods, followed by a call to + * {@link #toComparison} to get the result.</p> + */ + public CompareToBuilder() { + super(); + comparison = 0; + } + + //----------------------------------------------------------------------- + /** + * <p>Compares two <code>Object</code>s via reflection.</p> + * + * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code> + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.</p> + * + * <ul> + * <li>Static fields will not be compared</li> + * <li>Transient members will be not be compared, as they are likely derived + * fields</li> + * <li>Superclass fields will be compared</li> + * </ul> + * + * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>, + * they are considered equal.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @return a negative integer, zero, or a positive integer as <code>lhs</code> + * is less than, equal to, or greater than <code>rhs</code> + * @throws NullPointerException if either (but not both) parameters are + * <code>null</code> + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + */ + public static int reflectionCompare(Object lhs, Object rhs) { + return reflectionCompare(lhs, rhs, false, null); + } + + /** + * <p>Compares two <code>Object</code>s via reflection.</p> + * + * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code> + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.</p> + * + * <ul> + * <li>Static fields will not be compared</li> + * <li>If <code>compareTransients</code> is <code>true</code>, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.</li> + * <li>Superclass fields will be compared</li> + * </ul> + * + * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>, + * they are considered equal.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param compareTransients whether to compare transient fields + * @return a negative integer, zero, or a positive integer as <code>lhs</code> + * is less than, equal to, or greater than <code>rhs</code> + * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code> + * (but not both) is <code>null</code> + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + */ + public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) { + return reflectionCompare(lhs, rhs, compareTransients, null); + } + + /** + * <p>Compares two <code>Object</code>s via reflection.</p> + * + * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code> + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.</p> + * + * <ul> + * <li>Static fields will not be compared</li> + * <li>If <code>compareTransients</code> is <code>true</code>, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.</li> + * <li>Superclass fields will be compared</li> + * </ul> + * + * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>, + * they are considered equal.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields Collection of String fields to exclude + * @return a negative integer, zero, or a positive integer as <code>lhs</code> + * is less than, equal to, or greater than <code>rhs</code> + * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code> + * (but not both) is <code>null</code> + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, Collection<String> excludeFields) { + return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + * <p>Compares two <code>Object</code>s via reflection.</p> + * + * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code> + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.</p> + * + * <ul> + * <li>Static fields will not be compared</li> + * <li>If <code>compareTransients</code> is <code>true</code>, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.</li> + * <li>Superclass fields will be compared</li> + * </ul> + * + * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>, + * they are considered equal.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields array of fields to exclude + * @return a negative integer, zero, or a positive integer as <code>lhs</code> + * is less than, equal to, or greater than <code>rhs</code> + * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code> + * (but not both) is <code>null</code> + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, String... excludeFields) { + return reflectionCompare(lhs, rhs, false, null, excludeFields); + } + + /** + * <p>Compares two <code>Object</code>s via reflection.</p> + * + * <p>Fields can be private, thus <code>AccessibleObject.setAccessible</code> + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.</p> + * + * <ul> + * <li>Static fields will not be compared</li> + * <li>If the <code>compareTransients</code> is <code>true</code>, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.</li> + * <li>Compares superclass fields up to and including <code>reflectUpToClass</code>. + * If <code>reflectUpToClass</code> is <code>null</code>, compares all superclass fields.</li> + * </ul> + * + * <p>If both <code>lhs</code> and <code>rhs</code> are <code>null</code>, + * they are considered equal.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param compareTransients whether to compare transient fields + * @param reflectUpToClass last superclass for which fields are compared + * @param excludeFields fields to exclude + * @return a negative integer, zero, or a positive integer as <code>lhs</code> + * is less than, equal to, or greater than <code>rhs</code> + * @throws NullPointerException if either <code>lhs</code> or <code>rhs</code> + * (but not both) is <code>null</code> + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + * @since 2.2 (2.0 as <code>reflectionCompare(Object, Object, boolean, Class)</code>) + */ + public static int reflectionCompare( + Object lhs, + Object rhs, + boolean compareTransients, + Class<?> reflectUpToClass, + String... excludeFields) { + + if (lhs == rhs) { + return 0; + } + if (lhs == null || rhs == null) { + throw new NullPointerException(); + } + Class<?> lhsClazz = lhs.getClass(); + if (!lhsClazz.isInstance(rhs)) { + throw new ClassCastException(); + } + CompareToBuilder compareToBuilder = new CompareToBuilder(); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); + while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) { + lhsClazz = lhsClazz.getSuperclass(); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); + } + return compareToBuilder.toComparison(); + } + + /** + * <p>Appends to <code>builder</code> the comparison of <code>lhs</code> + * to <code>rhs</code> using the fields defined in <code>clazz</code>.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param clazz <code>Class</code> that defines fields to be compared + * @param builder <code>CompareToBuilder</code> to append to + * @param useTransients whether to compare transient fields + * @param excludeFields fields to exclude + */ + private static void reflectionAppend( + Object lhs, + Object rhs, + Class<?> clazz, + CompareToBuilder builder, + boolean useTransients, + String[] excludeFields) { + + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length && builder.comparison == 0; i++) { + Field f = fields[i]; + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(f.getModifiers())) + && (!Modifier.isStatic(f.getModifiers()))) { + try { + builder.append(f.get(lhs), f.get(rhs)); + } catch (IllegalAccessException e) { + // This can't happen. Would get a Security exception instead. + // Throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } + + //----------------------------------------------------------------------- + /** + * <p>Appends to the <code>builder</code> the <code>compareTo(Object)</code> + * result of the superclass.</p> + * + * @param superCompareTo result of calling <code>super.compareTo(Object)</code> + * @return this - used to chain append calls + * @since 2.0 + */ + public CompareToBuilder appendSuper(int superCompareTo) { + if (comparison != 0) { + return this; + } + comparison = superCompareTo; + return this; + } + + //----------------------------------------------------------------------- + /** + * <p>Appends to the <code>builder</code> the comparison of + * two <code>Object</code>s.</p> + * + * <ol> + * <li>Check if <code>lhs == rhs</code></li> + * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>, + * a <code>null</code> object is less than a non-<code>null</code> object</li> + * <li>Check the object contents</li> + * </ol> + * + * <p><code>lhs</code> must either be an array or implement {@link Comparable}.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @return this - used to chain append calls + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + */ + public CompareToBuilder append(Object lhs, Object rhs) { + return append(lhs, rhs, null); + } + + /** + * <p>Appends to the <code>builder</code> the comparison of + * two <code>Object</code>s.</p> + * + * <ol> + * <li>Check if <code>lhs == rhs</code></li> + * <li>Check if either <code>lhs</code> or <code>rhs</code> is <code>null</code>, + * a <code>null</code> object is less than a non-<code>null</code> object</li> + * <li>Check the object contents</li> + * </ol> + * + * <p>If <code>lhs</code> is an array, array comparison methods will be used. + * Otherwise <code>comparator</code> will be used to compare the objects. + * If <code>comparator</code> is <code>null</code>, <code>lhs</code> must + * implement {@link Comparable} instead.</p> + * + * @param lhs left-hand object + * @param rhs right-hand object + * @param comparator <code>Comparator</code> used to compare the objects, + * <code>null</code> means treat lhs as <code>Comparable</code> + * @return this - used to chain append calls + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + * @since 2.0 + */ + public CompareToBuilder append(Object lhs, Object rhs, Comparator<?> comparator) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.getClass().isArray()) { + // switch on type of array, to dispatch to the correct handler + // handles multi dimensional arrays + // throws a ClassCastException if rhs is not the correct array type + if (lhs instanceof long[]) { + append((long[]) lhs, (long[]) rhs); + } else if (lhs instanceof int[]) { + append((int[]) lhs, (int[]) rhs); + } else if (lhs instanceof short[]) { + append((short[]) lhs, (short[]) rhs); + } else if (lhs instanceof char[]) { + append((char[]) lhs, (char[]) rhs); + } else if (lhs instanceof byte[]) { + append((byte[]) lhs, (byte[]) rhs); + } else if (lhs instanceof double[]) { + append((double[]) lhs, (double[]) rhs); + } else if (lhs instanceof float[]) { + append((float[]) lhs, (float[]) rhs); + } else if (lhs instanceof boolean[]) { + append((boolean[]) lhs, (boolean[]) rhs); + } else { + // not an array of primitives + // throws a ClassCastException if rhs is not an array + append((Object[]) lhs, (Object[]) rhs, comparator); + } + } else { + // the simple case, not an array, just test the element + if (comparator == null) { + @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc + final Comparable<Object> comparable = (Comparable<Object>) lhs; + comparison = comparable.compareTo(rhs); + } else { + @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc + final Comparator<Object> comparator2 = (Comparator<Object>) comparator; + comparison = comparator2.compare(lhs, rhs); + } + } + return this; + } + + //------------------------------------------------------------------------- + /** + * Appends to the <code>builder</code> the comparison of + * two <code>long</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(long lhs, long rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the <code>builder</code> the comparison of + * two <code>int</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(int lhs, int rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the <code>builder</code> the comparison of + * two <code>short</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(short lhs, short rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the <code>builder</code> the comparison of + * two <code>char</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(char lhs, char rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the <code>builder</code> the comparison of + * two <code>byte</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(byte lhs, byte rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * <p>Appends to the <code>builder</code> the comparison of + * two <code>double</code>s.</p> + * + * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p> + * + * <p>It is compatible with the hash code generated by + * <code>HashCodeBuilder</code>.</p> + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(double lhs, double rhs) { + if (comparison != 0) { + return this; + } + comparison = Double.compare(lhs, rhs); + return this; + } + + /** + * <p>Appends to the <code>builder</code> the comparison of + * two <code>float</code>s.</p> + * + * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p> + * + * <p>It is compatible with the hash code generated by + * <code>HashCodeBuilder</code>.</p> + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(float lhs, float rhs) { + if (comparison != 0) { + return this; + } + comparison = Float.compare(lhs, rhs); + return this; + } + + /** + * Appends to the <code>builder</code> the comparison of + * two <code>booleans</code>s. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(boolean lhs, boolean rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == false) { + comparison = -1; + } else { + comparison = +1; + } + return this; + } + + //----------------------------------------------------------------------- + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>Object</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a short length array is less than a long length array</li> + * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li> + * </ol> + * + * <p>This method will also will be called for the top level of multi-dimensional, + * ragged, and multi-typed arrays.</p> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + */ + public CompareToBuilder append(Object[] lhs, Object[] rhs) { + return append(lhs, rhs, null); + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>Object</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a short length array is less than a long length array</li> + * <li>Check array contents element by element using {@link #append(Object, Object, Comparator)}</li> + * </ol> + * + * <p>This method will also will be called for the top level of multi-dimensional, + * ragged, and multi-typed arrays.</p> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @param comparator <code>Comparator</code> to use to compare the array elements, + * <code>null</code> means to treat <code>lhs</code> elements as <code>Comparable</code>. + * @return this - used to chain append calls + * @throws ClassCastException if <code>rhs</code> is not assignment-compatible + * with <code>lhs</code> + * @since 2.0 + */ + public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator<?> comparator) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i], comparator); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>long</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(long, long)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(long[] lhs, long[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>int</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(int, int)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(int[] lhs, int[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>short</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(short, short)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(short[] lhs, short[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>char</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(char, char)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(char[] lhs, char[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>byte</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(byte, byte)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(byte[] lhs, byte[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>double</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(double, double)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(double[] lhs, double[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>float</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(float, float)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(float[] lhs, float[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Appends to the <code>builder</code> the deep comparison of + * two <code>boolean</code> arrays.</p> + * + * <ol> + * <li>Check if arrays are the same using <code>==</code></li> + * <li>Check if for <code>null</code>, <code>null</code> is less than non-<code>null</code></li> + * <li>Check array length, a shorter length array is less than a longer length array</li> + * <li>Check array contents element by element using {@link #append(boolean, boolean)}</li> + * </ol> + * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(boolean[] lhs, boolean[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Returns a negative integer, a positive integer, or zero as + * the <code>builder</code> has judged the "left-hand" side + * as less than, greater than, or equal to the "right-hand" + * side. + * + * @return final comparison result + */ + public int toComparison() { + return comparison; + } + + /** + * Returns a negative integer, a positive integer, or zero as + * the <code>builder</code> has judged the "left-hand" side + * as less than, greater than, or equal to the "right-hand" + * side. + * + * @return final comparison result + * + * @since 3.0 + */ + public Integer build() { + return Integer.valueOf(toComparison()); + } +} + diff --git a/src/org/apache/commons/lang3/builder/EqualsBuilder.java b/src/org/apache/commons/lang3/builder/EqualsBuilder.java index d619495..2c8e5f9 100644 --- a/src/org/apache/commons/lang3/builder/EqualsBuilder.java +++ b/src/org/apache/commons/lang3/builder/EqualsBuilder.java @@ -1,944 +1,944 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * <p>Assists in implementing {@link Object#equals(Object)} methods.</p>
- *
- * <p> This class provides methods to build a good equals method for any
- * class. It follows rules laid out in
- * <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a>
- * , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>,
- * <code>floats</code>, and arrays can be tricky. Also, making sure that
- * <code>equals()</code> and <code>hashCode()</code> are consistent can be
- * difficult.</p>
- *
- * <p>Two Objects that compare as equals must generate the same hash code,
- * but two Objects with the same hash code do not have to be equal.</p>
- *
- * <p>All relevant fields should be included in the calculation of equals.
- * Derived fields may be ignored. In particular, any field used in
- * generating a hash code must be used in the equals method, and vice
- * versa.</p>
- *
- * <p>Typical use for the code is as follows:</p>
- * <pre>
- * public boolean equals(Object obj) {
- * if (obj == null) { return false; }
- * if (obj == this) { return true; }
- * if (obj.getClass() != getClass()) {
- * return false;
- * }
- * MyClass rhs = (MyClass) obj;
- * return new EqualsBuilder()
- * .appendSuper(super.equals(obj))
- * .append(field1, rhs.field1)
- * .append(field2, rhs.field2)
- * .append(field3, rhs.field3)
- * .isEquals();
- * }
- * </pre>
- *
- * <p> Alternatively, there is a method that uses reflection to determine
- * the fields to test. Because these fields are usually private, the method,
- * <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code> to
- * change the visibility of the fields. This will fail under a security
- * manager, unless the appropriate permissions are set up correctly. It is
- * also slower than testing explicitly.</p>
- *
- * <p> A typical invocation for this method would look like:</p>
- * <pre>
- * public boolean equals(Object obj) {
- * return EqualsBuilder.reflectionEquals(this, obj);
- * }
- * </pre>
- *
- * @since 1.0
- * @version $Id: EqualsBuilder.java 1091531 2011-04-12 18:29:49Z ggregory $
- */
-public class EqualsBuilder implements Builder<Boolean> {
-
- /**
- * <p>
- * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
- * </p>
- *
- * @since 3.0
- */
- private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal<Set<Pair<IDKey, IDKey>>>();
-
- /*
- * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
- * we are in the process of calculating.
- *
- * So we generate a one-to-one mapping from the original object to a new object.
- *
- * Now HashSet uses equals() to determine if two elements with the same hashcode really
- * are equal, so we also need to ensure that the replacement objects are only equal
- * if the original objects are identical.
- *
- * The original implementation (2.4 and before) used the System.indentityHashCode()
- * method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
- *
- * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
- * to disambiguate the duplicate ids.
- */
-
- /**
- * <p>
- * Returns the registry of object pairs being traversed by the reflection
- * methods in the current thread.
- * </p>
- *
- * @return Set the registry of objects being traversed
- * @since 3.0
- */
- static Set<Pair<IDKey, IDKey>> getRegistry() {
- return REGISTRY.get();
- }
-
- /**
- * <p>
- * Converters value pair into a register pair.
- * </p>
- *
- * @param lhs <code>this</code> object
- * @param rhs the other object
- *
- * @return the pair
- */
- static Pair<IDKey, IDKey> getRegisterPair(Object lhs, Object rhs) {
- IDKey left = new IDKey(lhs);
- IDKey right = new IDKey(rhs);
- return Pair.of(left, right);
- }
-
- /**
- * <p>
- * Returns <code>true</code> if the registry contains the given object pair.
- * Used by the reflection methods to avoid infinite loops.
- * Objects might be swapped therefore a check is needed if the object pair
- * is registered in given or swapped order.
- * </p>
- *
- * @param lhs <code>this</code> object to lookup in registry
- * @param rhs the other object to lookup on registry
- * @return boolean <code>true</code> if the registry contains the given object.
- * @since 3.0
- */
- static boolean isRegistered(Object lhs, Object rhs) {
- Set<Pair<IDKey, IDKey>> registry = getRegistry();
- Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
- Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getLeft(), pair.getRight());
-
- return registry != null
- && (registry.contains(pair) || registry.contains(swappedPair));
- }
-
- /**
- * <p>
- * Registers the given object pair.
- * Used by the reflection methods to avoid infinite loops.
- * </p>
- *
- * @param lhs <code>this</code> object to register
- * @param rhs the other object to register
- */
- static void register(Object lhs, Object rhs) {
- synchronized (EqualsBuilder.class) {
- if (getRegistry() == null) {
- REGISTRY.set(new HashSet<Pair<IDKey, IDKey>>());
- }
- }
-
- Set<Pair<IDKey, IDKey>> registry = getRegistry();
- Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
- registry.add(pair);
- }
-
- /**
- * <p>
- * Unregisters the given object pair.
- * </p>
- *
- * <p>
- * Used by the reflection methods to avoid infinite loops.
- *
- * @param lhs <code>this</code> object to unregister
- * @param rhs the other object to unregister
- * @since 3.0
- */
- static void unregister(Object lhs, Object rhs) {
- Set<Pair<IDKey, IDKey>> registry = getRegistry();
- if (registry != null) {
- Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs);
- registry.remove(pair);
- synchronized (EqualsBuilder.class) {
- //read again
- registry = getRegistry();
- if (registry != null && registry.isEmpty()) {
- REGISTRY.remove();
- }
- }
- }
- }
-
- /**
- * If the fields tested are equals.
- * The default value is <code>true</code>.
- */
- private boolean isEquals = true;
-
- /**
- * <p>Constructor for EqualsBuilder.</p>
- *
- * <p>Starts off assuming that equals is <code>true</code>.</p>
- * @see Object#equals(Object)
- */
- public EqualsBuilder() {
- // do nothing for now.
- }
-
- //-------------------------------------------------------------------------
-
- /**
- * <p>This method uses reflection to determine if the two <code>Object</code>s
- * are equal.</p>
- *
- * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
- * fields. This means that it will throw a security exception if run under
- * a security manager, if the permissions are not set up correctly. It is also
- * not as efficient as testing explicitly.</p>
- *
- * <p>Transient members will be not be tested, as they are likely derived
- * fields, and not part of the value of the Object.</p>
- *
- * <p>Static fields will not be tested. Superclass fields will be included.</p>
- *
- * @param lhs <code>this</code> object
- * @param rhs the other object
- * @param excludeFields Collection of String field names to exclude from testing
- * @return <code>true</code> if the two Objects have tested equals.
- */
- public static boolean reflectionEquals(Object lhs, Object rhs, Collection<String> excludeFields) {
- return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
- }
-
- /**
- * <p>This method uses reflection to determine if the two <code>Object</code>s
- * are equal.</p>
- *
- * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
- * fields. This means that it will throw a security exception if run under
- * a security manager, if the permissions are not set up correctly. It is also
- * not as efficient as testing explicitly.</p>
- *
- * <p>Transient members will be not be tested, as they are likely derived
- * fields, and not part of the value of the Object.</p>
- *
- * <p>Static fields will not be tested. Superclass fields will be included.</p>
- *
- * @param lhs <code>this</code> object
- * @param rhs the other object
- * @param excludeFields array of field names to exclude from testing
- * @return <code>true</code> if the two Objects have tested equals.
- */
- public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields) {
- return reflectionEquals(lhs, rhs, false, null, excludeFields);
- }
-
- /**
- * <p>This method uses reflection to determine if the two <code>Object</code>s
- * are equal.</p>
- *
- * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
- * fields. This means that it will throw a security exception if run under
- * a security manager, if the permissions are not set up correctly. It is also
- * not as efficient as testing explicitly.</p>
- *
- * <p>If the TestTransients parameter is set to <code>true</code>, transient
- * members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the <code>Object</code>.</p>
- *
- * <p>Static fields will not be tested. Superclass fields will be included.</p>
- *
- * @param lhs <code>this</code> object
- * @param rhs the other object
- * @param testTransients whether to include transient fields
- * @return <code>true</code> if the two Objects have tested equals.
- */
- public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) {
- return reflectionEquals(lhs, rhs, testTransients, null);
- }
-
- /**
- * <p>This method uses reflection to determine if the two <code>Object</code>s
- * are equal.</p>
- *
- * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private
- * fields. This means that it will throw a security exception if run under
- * a security manager, if the permissions are not set up correctly. It is also
- * not as efficient as testing explicitly.</p>
- *
- * <p>If the testTransients parameter is set to <code>true</code>, transient
- * members will be tested, otherwise they are ignored, as they are likely
- * derived fields, and not part of the value of the <code>Object</code>.</p>
- *
- * <p>Static fields will not be included. Superclass fields will be appended
- * up to and including the specified superclass. A null superclass is treated
- * as java.lang.Object.</p>
- *
- * @param lhs <code>this</code> object
- * @param rhs the other object
- * @param testTransients whether to include transient fields
- * @param reflectUpToClass the superclass to reflect up to (inclusive),
- * may be <code>null</code>
- * @param excludeFields array of field names to exclude from testing
- * @return <code>true</code> if the two Objects have tested equals.
- * @since 2.0
- */
- public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass,
- String... excludeFields) {
- if (lhs == rhs) {
- return true;
- }
- if (lhs == null || rhs == null) {
- return false;
- }
- // Find the leaf class since there may be transients in the leaf
- // class or in classes between the leaf and root.
- // If we are not testing transients or a subclass has no ivars,
- // then a subclass can test equals to a superclass.
- Class<?> lhsClass = lhs.getClass();
- Class<?> rhsClass = rhs.getClass();
- Class<?> testClass;
- if (lhsClass.isInstance(rhs)) {
- testClass = lhsClass;
- if (!rhsClass.isInstance(lhs)) {
- // rhsClass is a subclass of lhsClass
- testClass = rhsClass;
- }
- } else if (rhsClass.isInstance(lhs)) {
- testClass = rhsClass;
- if (!lhsClass.isInstance(rhs)) {
- // lhsClass is a subclass of rhsClass
- testClass = lhsClass;
- }
- } else {
- // The two classes are not related.
- return false;
- }
- EqualsBuilder equalsBuilder = new EqualsBuilder();
- try {
- reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
- while (testClass.getSuperclass() != null && testClass != reflectUpToClass) {
- testClass = testClass.getSuperclass();
- reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields);
- }
- } catch (IllegalArgumentException e) {
- // In this case, we tried to test a subclass vs. a superclass and
- // the subclass has ivars or the ivars are transient and
- // we are testing transients.
- // If a subclass has ivars that we are trying to test them, we get an
- // exception and we know that the objects are not equal.
- return false;
- }
- return equalsBuilder.isEquals();
- }
-
- /**
- * <p>Appends the fields and values defined by the given object of the
- * given Class.</p>
- *
- * @param lhs the left hand object
- * @param rhs the right hand object
- * @param clazz the class to append details of
- * @param builder the builder to append to
- * @param useTransients whether to test transient fields
- * @param excludeFields array of field names to exclude from testing
- */
- private static void reflectionAppend(
- Object lhs,
- Object rhs,
- Class<?> clazz,
- EqualsBuilder builder,
- boolean useTransients,
- String[] excludeFields) {
-
- if (isRegistered(lhs, rhs)) {
- return;
- }
-
- try {
- register(lhs, rhs);
- Field[] fields = clazz.getDeclaredFields();
- AccessibleObject.setAccessible(fields, true);
- for (int i = 0; i < fields.length && builder.isEquals; i++) {
- Field f = fields[i];
- if (!ArrayUtils.contains(excludeFields, f.getName())
- && (f.getName().indexOf('$') == -1)
- && (useTransients || !Modifier.isTransient(f.getModifiers()))
- && (!Modifier.isStatic(f.getModifiers()))) {
- try {
- builder.append(f.get(lhs), f.get(rhs));
- } catch (IllegalAccessException e) {
- //this can't happen. Would get a Security exception instead
- //throw a runtime exception in case the impossible happens.
- throw new InternalError("Unexpected IllegalAccessException");
- }
- }
- }
- } finally {
- unregister(lhs, rhs);
- }
- }
-
- //-------------------------------------------------------------------------
-
- /**
- * <p>Adds the result of <code>super.equals()</code> to this builder.</p>
- *
- * @param superEquals the result of calling <code>super.equals()</code>
- * @return EqualsBuilder - used to chain calls.
- * @since 2.0
- */
- public EqualsBuilder appendSuper(boolean superEquals) {
- if (isEquals == false) {
- return this;
- }
- isEquals = superEquals;
- return this;
- }
-
- //-------------------------------------------------------------------------
-
- /**
- * <p>Test if two <code>Object</code>s are equal using their
- * <code>equals</code> method.</p>
- *
- * @param lhs the left hand object
- * @param rhs the right hand object
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(Object lhs, Object rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- Class<?> lhsClass = lhs.getClass();
- if (!lhsClass.isArray()) {
- // The simple case, not an array, just test the element
- isEquals = lhs.equals(rhs);
- } else if (lhs.getClass() != rhs.getClass()) {
- // Here when we compare different dimensions, for example: a boolean[][] to a boolean[]
- this.setEquals(false);
- }
- // 'Switch' on type of array, to dispatch to the correct handler
- // This handles multi dimensional arrays of the same depth
- else if (lhs instanceof long[]) {
- append((long[]) lhs, (long[]) rhs);
- } else if (lhs instanceof int[]) {
- append((int[]) lhs, (int[]) rhs);
- } else if (lhs instanceof short[]) {
- append((short[]) lhs, (short[]) rhs);
- } else if (lhs instanceof char[]) {
- append((char[]) lhs, (char[]) rhs);
- } else if (lhs instanceof byte[]) {
- append((byte[]) lhs, (byte[]) rhs);
- } else if (lhs instanceof double[]) {
- append((double[]) lhs, (double[]) rhs);
- } else if (lhs instanceof float[]) {
- append((float[]) lhs, (float[]) rhs);
- } else if (lhs instanceof boolean[]) {
- append((boolean[]) lhs, (boolean[]) rhs);
- } else {
- // Not an array of primitives
- append((Object[]) lhs, (Object[]) rhs);
- }
- return this;
- }
-
- /**
- * <p>
- * Test if two <code>long</code> s are equal.
- * </p>
- *
- * @param lhs
- * the left hand <code>long</code>
- * @param rhs
- * the right hand <code>long</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(long lhs, long rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Test if two <code>int</code>s are equal.</p>
- *
- * @param lhs the left hand <code>int</code>
- * @param rhs the right hand <code>int</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(int lhs, int rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Test if two <code>short</code>s are equal.</p>
- *
- * @param lhs the left hand <code>short</code>
- * @param rhs the right hand <code>short</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(short lhs, short rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Test if two <code>char</code>s are equal.</p>
- *
- * @param lhs the left hand <code>char</code>
- * @param rhs the right hand <code>char</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(char lhs, char rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Test if two <code>byte</code>s are equal.</p>
- *
- * @param lhs the left hand <code>byte</code>
- * @param rhs the right hand <code>byte</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(byte lhs, byte rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Test if two <code>double</code>s are equal by testing that the
- * pattern of bits returned by <code>doubleToLong</code> are equal.</p>
- *
- * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
- *
- * <p>It is compatible with the hash code generated by
- * <code>HashCodeBuilder</code>.</p>
- *
- * @param lhs the left hand <code>double</code>
- * @param rhs the right hand <code>double</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(double lhs, double rhs) {
- if (isEquals == false) {
- return this;
- }
- return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs));
- }
-
- /**
- * <p>Test if two <code>float</code>s are equal byt testing that the
- * pattern of bits returned by doubleToLong are equal.</p>
- *
- * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p>
- *
- * <p>It is compatible with the hash code generated by
- * <code>HashCodeBuilder</code>.</p>
- *
- * @param lhs the left hand <code>float</code>
- * @param rhs the right hand <code>float</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(float lhs, float rhs) {
- if (isEquals == false) {
- return this;
- }
- return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs));
- }
-
- /**
- * <p>Test if two <code>booleans</code>s are equal.</p>
- *
- * @param lhs the left hand <code>boolean</code>
- * @param rhs the right hand <code>boolean</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(boolean lhs, boolean rhs) {
- if (isEquals == false) {
- return this;
- }
- isEquals = (lhs == rhs);
- return this;
- }
-
- /**
- * <p>Performs a deep comparison of two <code>Object</code> arrays.</p>
- *
- * <p>This also will be called for the top level of
- * multi-dimensional, ragged, and multi-typed arrays.</p>
- *
- * @param lhs the left hand <code>Object[]</code>
- * @param rhs the right hand <code>Object[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(Object[] lhs, Object[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>long</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(long, long)} is used.</p>
- *
- * @param lhs the left hand <code>long[]</code>
- * @param rhs the right hand <code>long[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(long[] lhs, long[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>int</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(int, int)} is used.</p>
- *
- * @param lhs the left hand <code>int[]</code>
- * @param rhs the right hand <code>int[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(int[] lhs, int[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>short</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(short, short)} is used.</p>
- *
- * @param lhs the left hand <code>short[]</code>
- * @param rhs the right hand <code>short[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(short[] lhs, short[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>char</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(char, char)} is used.</p>
- *
- * @param lhs the left hand <code>char[]</code>
- * @param rhs the right hand <code>char[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(char[] lhs, char[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>byte</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(byte, byte)} is used.</p>
- *
- * @param lhs the left hand <code>byte[]</code>
- * @param rhs the right hand <code>byte[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(byte[] lhs, byte[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>double</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(double, double)} is used.</p>
- *
- * @param lhs the left hand <code>double[]</code>
- * @param rhs the right hand <code>double[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(double[] lhs, double[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>float</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(float, float)} is used.</p>
- *
- * @param lhs the left hand <code>float[]</code>
- * @param rhs the right hand <code>float[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(float[] lhs, float[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Deep comparison of array of <code>boolean</code>. Length and all
- * values are compared.</p>
- *
- * <p>The method {@link #append(boolean, boolean)} is used.</p>
- *
- * @param lhs the left hand <code>boolean[]</code>
- * @param rhs the right hand <code>boolean[]</code>
- * @return EqualsBuilder - used to chain calls.
- */
- public EqualsBuilder append(boolean[] lhs, boolean[] rhs) {
- if (isEquals == false) {
- return this;
- }
- if (lhs == rhs) {
- return this;
- }
- if (lhs == null || rhs == null) {
- this.setEquals(false);
- return this;
- }
- if (lhs.length != rhs.length) {
- this.setEquals(false);
- return this;
- }
- for (int i = 0; i < lhs.length && isEquals; ++i) {
- append(lhs[i], rhs[i]);
- }
- return this;
- }
-
- /**
- * <p>Returns <code>true</code> if the fields that have been checked
- * are all equal.</p>
- *
- * @return boolean
- */
- public boolean isEquals() {
- return this.isEquals;
- }
-
- /**
- * <p>Returns <code>true</code> if the fields that have been checked
- * are all equal.</p>
- *
- * @return <code>true</code> if all of the fields that have been checked
- * are equal, <code>false</code> otherwise.
- *
- * @since 3.0
- */
- public Boolean build() {
- return Boolean.valueOf(isEquals());
- }
-
- /**
- * Sets the <code>isEquals</code> value.
- *
- * @param isEquals The value to set.
- * @since 2.1
- */
- protected void setEquals(boolean isEquals) {
- this.isEquals = isEquals;
- }
-
- /**
- * Reset the EqualsBuilder so you can use the same object again
- * @since 2.5
- */
- public void reset() {
- this.isEquals = true;
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Pair; + +/** + * <p>Assists in implementing {@link Object#equals(Object)} methods.</p> + * + * <p> This class provides methods to build a good equals method for any + * class. It follows rules laid out in + * <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> + * , by Joshua Bloch. In particular the rule for comparing <code>doubles</code>, + * <code>floats</code>, and arrays can be tricky. Also, making sure that + * <code>equals()</code> and <code>hashCode()</code> are consistent can be + * difficult.</p> + * + * <p>Two Objects that compare as equals must generate the same hash code, + * but two Objects with the same hash code do not have to be equal.</p> + * + * <p>All relevant fields should be included in the calculation of equals. + * Derived fields may be ignored. In particular, any field used in + * generating a hash code must be used in the equals method, and vice + * versa.</p> + * + * <p>Typical use for the code is as follows:</p> + * <pre> + * public boolean equals(Object obj) { + * if (obj == null) { return false; } + * if (obj == this) { return true; } + * if (obj.getClass() != getClass()) { + * return false; + * } + * MyClass rhs = (MyClass) obj; + * return new EqualsBuilder() + * .appendSuper(super.equals(obj)) + * .append(field1, rhs.field1) + * .append(field2, rhs.field2) + * .append(field3, rhs.field3) + * .isEquals(); + * } + * </pre> + * + * <p> Alternatively, there is a method that uses reflection to determine + * the fields to test. Because these fields are usually private, the method, + * <code>reflectionEquals</code>, uses <code>AccessibleObject.setAccessible</code> to + * change the visibility of the fields. This will fail under a security + * manager, unless the appropriate permissions are set up correctly. It is + * also slower than testing explicitly.</p> + * + * <p> A typical invocation for this method would look like:</p> + * <pre> + * public boolean equals(Object obj) { + * return EqualsBuilder.reflectionEquals(this, obj); + * } + * </pre> + * + * @since 1.0 + * @version $Id: EqualsBuilder.java 1091531 2011-04-12 18:29:49Z ggregory $ + */ +public class EqualsBuilder implements Builder<Boolean> { + + /** + * <p> + * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops. + * </p> + * + * @since 3.0 + */ + private static final ThreadLocal<Set<Pair<IDKey, IDKey>>> REGISTRY = new ThreadLocal<Set<Pair<IDKey, IDKey>>>(); + + /* + * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode() + * we are in the process of calculating. + * + * So we generate a one-to-one mapping from the original object to a new object. + * + * Now HashSet uses equals() to determine if two elements with the same hashcode really + * are equal, so we also need to ensure that the replacement objects are only equal + * if the original objects are identical. + * + * The original implementation (2.4 and before) used the System.indentityHashCode() + * method - however this is not guaranteed to generate unique ids (e.g. LANG-459) + * + * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey) + * to disambiguate the duplicate ids. + */ + + /** + * <p> + * Returns the registry of object pairs being traversed by the reflection + * methods in the current thread. + * </p> + * + * @return Set the registry of objects being traversed + * @since 3.0 + */ + static Set<Pair<IDKey, IDKey>> getRegistry() { + return REGISTRY.get(); + } + + /** + * <p> + * Converters value pair into a register pair. + * </p> + * + * @param lhs <code>this</code> object + * @param rhs the other object + * + * @return the pair + */ + static Pair<IDKey, IDKey> getRegisterPair(Object lhs, Object rhs) { + IDKey left = new IDKey(lhs); + IDKey right = new IDKey(rhs); + return Pair.of(left, right); + } + + /** + * <p> + * Returns <code>true</code> if the registry contains the given object pair. + * Used by the reflection methods to avoid infinite loops. + * Objects might be swapped therefore a check is needed if the object pair + * is registered in given or swapped order. + * </p> + * + * @param lhs <code>this</code> object to lookup in registry + * @param rhs the other object to lookup on registry + * @return boolean <code>true</code> if the registry contains the given object. + * @since 3.0 + */ + static boolean isRegistered(Object lhs, Object rhs) { + Set<Pair<IDKey, IDKey>> registry = getRegistry(); + Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs); + Pair<IDKey, IDKey> swappedPair = Pair.of(pair.getLeft(), pair.getRight()); + + return registry != null + && (registry.contains(pair) || registry.contains(swappedPair)); + } + + /** + * <p> + * Registers the given object pair. + * Used by the reflection methods to avoid infinite loops. + * </p> + * + * @param lhs <code>this</code> object to register + * @param rhs the other object to register + */ + static void register(Object lhs, Object rhs) { + synchronized (EqualsBuilder.class) { + if (getRegistry() == null) { + REGISTRY.set(new HashSet<Pair<IDKey, IDKey>>()); + } + } + + Set<Pair<IDKey, IDKey>> registry = getRegistry(); + Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs); + registry.add(pair); + } + + /** + * <p> + * Unregisters the given object pair. + * </p> + * + * <p> + * Used by the reflection methods to avoid infinite loops. + * + * @param lhs <code>this</code> object to unregister + * @param rhs the other object to unregister + * @since 3.0 + */ + static void unregister(Object lhs, Object rhs) { + Set<Pair<IDKey, IDKey>> registry = getRegistry(); + if (registry != null) { + Pair<IDKey, IDKey> pair = getRegisterPair(lhs, rhs); + registry.remove(pair); + synchronized (EqualsBuilder.class) { + //read again + registry = getRegistry(); + if (registry != null && registry.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * If the fields tested are equals. + * The default value is <code>true</code>. + */ + private boolean isEquals = true; + + /** + * <p>Constructor for EqualsBuilder.</p> + * + * <p>Starts off assuming that equals is <code>true</code>.</p> + * @see Object#equals(Object) + */ + public EqualsBuilder() { + // do nothing for now. + } + + //------------------------------------------------------------------------- + + /** + * <p>This method uses reflection to determine if the two <code>Object</code>s + * are equal.</p> + * + * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.</p> + * + * <p>Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.</p> + * + * <p>Static fields will not be tested. Superclass fields will be included.</p> + * + * @param lhs <code>this</code> object + * @param rhs the other object + * @param excludeFields Collection of String field names to exclude from testing + * @return <code>true</code> if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, Collection<String> excludeFields) { + return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + * <p>This method uses reflection to determine if the two <code>Object</code>s + * are equal.</p> + * + * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.</p> + * + * <p>Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.</p> + * + * <p>Static fields will not be tested. Superclass fields will be included.</p> + * + * @param lhs <code>this</code> object + * @param rhs the other object + * @param excludeFields array of field names to exclude from testing + * @return <code>true</code> if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields) { + return reflectionEquals(lhs, rhs, false, null, excludeFields); + } + + /** + * <p>This method uses reflection to determine if the two <code>Object</code>s + * are equal.</p> + * + * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.</p> + * + * <p>If the TestTransients parameter is set to <code>true</code>, transient + * members will be tested, otherwise they are ignored, as they are likely + * derived fields, and not part of the value of the <code>Object</code>.</p> + * + * <p>Static fields will not be tested. Superclass fields will be included.</p> + * + * @param lhs <code>this</code> object + * @param rhs the other object + * @param testTransients whether to include transient fields + * @return <code>true</code> if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) { + return reflectionEquals(lhs, rhs, testTransients, null); + } + + /** + * <p>This method uses reflection to determine if the two <code>Object</code>s + * are equal.</p> + * + * <p>It uses <code>AccessibleObject.setAccessible</code> to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.</p> + * + * <p>If the testTransients parameter is set to <code>true</code>, transient + * members will be tested, otherwise they are ignored, as they are likely + * derived fields, and not part of the value of the <code>Object</code>.</p> + * + * <p>Static fields will not be included. Superclass fields will be appended + * up to and including the specified superclass. A null superclass is treated + * as java.lang.Object.</p> + * + * @param lhs <code>this</code> object + * @param rhs the other object + * @param testTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), + * may be <code>null</code> + * @param excludeFields array of field names to exclude from testing + * @return <code>true</code> if the two Objects have tested equals. + * @since 2.0 + */ + public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class<?> reflectUpToClass, + String... excludeFields) { + if (lhs == rhs) { + return true; + } + if (lhs == null || rhs == null) { + return false; + } + // Find the leaf class since there may be transients in the leaf + // class or in classes between the leaf and root. + // If we are not testing transients or a subclass has no ivars, + // then a subclass can test equals to a superclass. + Class<?> lhsClass = lhs.getClass(); + Class<?> rhsClass = rhs.getClass(); + Class<?> testClass; + if (lhsClass.isInstance(rhs)) { + testClass = lhsClass; + if (!rhsClass.isInstance(lhs)) { + // rhsClass is a subclass of lhsClass + testClass = rhsClass; + } + } else if (rhsClass.isInstance(lhs)) { + testClass = rhsClass; + if (!lhsClass.isInstance(rhs)) { + // lhsClass is a subclass of rhsClass + testClass = lhsClass; + } + } else { + // The two classes are not related. + return false; + } + EqualsBuilder equalsBuilder = new EqualsBuilder(); + try { + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); + while (testClass.getSuperclass() != null && testClass != reflectUpToClass) { + testClass = testClass.getSuperclass(); + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); + } + } catch (IllegalArgumentException e) { + // In this case, we tried to test a subclass vs. a superclass and + // the subclass has ivars or the ivars are transient and + // we are testing transients. + // If a subclass has ivars that we are trying to test them, we get an + // exception and we know that the objects are not equal. + return false; + } + return equalsBuilder.isEquals(); + } + + /** + * <p>Appends the fields and values defined by the given object of the + * given Class.</p> + * + * @param lhs the left hand object + * @param rhs the right hand object + * @param clazz the class to append details of + * @param builder the builder to append to + * @param useTransients whether to test transient fields + * @param excludeFields array of field names to exclude from testing + */ + private static void reflectionAppend( + Object lhs, + Object rhs, + Class<?> clazz, + EqualsBuilder builder, + boolean useTransients, + String[] excludeFields) { + + if (isRegistered(lhs, rhs)) { + return; + } + + try { + register(lhs, rhs); + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length && builder.isEquals; i++) { + Field f = fields[i]; + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(f.getModifiers())) + && (!Modifier.isStatic(f.getModifiers()))) { + try { + builder.append(f.get(lhs), f.get(rhs)); + } catch (IllegalAccessException e) { + //this can't happen. Would get a Security exception instead + //throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } finally { + unregister(lhs, rhs); + } + } + + //------------------------------------------------------------------------- + + /** + * <p>Adds the result of <code>super.equals()</code> to this builder.</p> + * + * @param superEquals the result of calling <code>super.equals()</code> + * @return EqualsBuilder - used to chain calls. + * @since 2.0 + */ + public EqualsBuilder appendSuper(boolean superEquals) { + if (isEquals == false) { + return this; + } + isEquals = superEquals; + return this; + } + + //------------------------------------------------------------------------- + + /** + * <p>Test if two <code>Object</code>s are equal using their + * <code>equals</code> method.</p> + * + * @param lhs the left hand object + * @param rhs the right hand object + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(Object lhs, Object rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + Class<?> lhsClass = lhs.getClass(); + if (!lhsClass.isArray()) { + // The simple case, not an array, just test the element + isEquals = lhs.equals(rhs); + } else if (lhs.getClass() != rhs.getClass()) { + // Here when we compare different dimensions, for example: a boolean[][] to a boolean[] + this.setEquals(false); + } + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays of the same depth + else if (lhs instanceof long[]) { + append((long[]) lhs, (long[]) rhs); + } else if (lhs instanceof int[]) { + append((int[]) lhs, (int[]) rhs); + } else if (lhs instanceof short[]) { + append((short[]) lhs, (short[]) rhs); + } else if (lhs instanceof char[]) { + append((char[]) lhs, (char[]) rhs); + } else if (lhs instanceof byte[]) { + append((byte[]) lhs, (byte[]) rhs); + } else if (lhs instanceof double[]) { + append((double[]) lhs, (double[]) rhs); + } else if (lhs instanceof float[]) { + append((float[]) lhs, (float[]) rhs); + } else if (lhs instanceof boolean[]) { + append((boolean[]) lhs, (boolean[]) rhs); + } else { + // Not an array of primitives + append((Object[]) lhs, (Object[]) rhs); + } + return this; + } + + /** + * <p> + * Test if two <code>long</code> s are equal. + * </p> + * + * @param lhs + * the left hand <code>long</code> + * @param rhs + * the right hand <code>long</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(long lhs, long rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Test if two <code>int</code>s are equal.</p> + * + * @param lhs the left hand <code>int</code> + * @param rhs the right hand <code>int</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(int lhs, int rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Test if two <code>short</code>s are equal.</p> + * + * @param lhs the left hand <code>short</code> + * @param rhs the right hand <code>short</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(short lhs, short rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Test if two <code>char</code>s are equal.</p> + * + * @param lhs the left hand <code>char</code> + * @param rhs the right hand <code>char</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(char lhs, char rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Test if two <code>byte</code>s are equal.</p> + * + * @param lhs the left hand <code>byte</code> + * @param rhs the right hand <code>byte</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(byte lhs, byte rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Test if two <code>double</code>s are equal by testing that the + * pattern of bits returned by <code>doubleToLong</code> are equal.</p> + * + * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p> + * + * <p>It is compatible with the hash code generated by + * <code>HashCodeBuilder</code>.</p> + * + * @param lhs the left hand <code>double</code> + * @param rhs the right hand <code>double</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(double lhs, double rhs) { + if (isEquals == false) { + return this; + } + return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs)); + } + + /** + * <p>Test if two <code>float</code>s are equal byt testing that the + * pattern of bits returned by doubleToLong are equal.</p> + * + * <p>This handles NaNs, Infinities, and <code>-0.0</code>.</p> + * + * <p>It is compatible with the hash code generated by + * <code>HashCodeBuilder</code>.</p> + * + * @param lhs the left hand <code>float</code> + * @param rhs the right hand <code>float</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(float lhs, float rhs) { + if (isEquals == false) { + return this; + } + return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs)); + } + + /** + * <p>Test if two <code>booleans</code>s are equal.</p> + * + * @param lhs the left hand <code>boolean</code> + * @param rhs the right hand <code>boolean</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(boolean lhs, boolean rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + * <p>Performs a deep comparison of two <code>Object</code> arrays.</p> + * + * <p>This also will be called for the top level of + * multi-dimensional, ragged, and multi-typed arrays.</p> + * + * @param lhs the left hand <code>Object[]</code> + * @param rhs the right hand <code>Object[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(Object[] lhs, Object[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>long</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(long, long)} is used.</p> + * + * @param lhs the left hand <code>long[]</code> + * @param rhs the right hand <code>long[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(long[] lhs, long[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>int</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(int, int)} is used.</p> + * + * @param lhs the left hand <code>int[]</code> + * @param rhs the right hand <code>int[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(int[] lhs, int[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>short</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(short, short)} is used.</p> + * + * @param lhs the left hand <code>short[]</code> + * @param rhs the right hand <code>short[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(short[] lhs, short[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>char</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(char, char)} is used.</p> + * + * @param lhs the left hand <code>char[]</code> + * @param rhs the right hand <code>char[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(char[] lhs, char[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>byte</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(byte, byte)} is used.</p> + * + * @param lhs the left hand <code>byte[]</code> + * @param rhs the right hand <code>byte[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(byte[] lhs, byte[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>double</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(double, double)} is used.</p> + * + * @param lhs the left hand <code>double[]</code> + * @param rhs the right hand <code>double[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(double[] lhs, double[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>float</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(float, float)} is used.</p> + * + * @param lhs the left hand <code>float[]</code> + * @param rhs the right hand <code>float[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(float[] lhs, float[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Deep comparison of array of <code>boolean</code>. Length and all + * values are compared.</p> + * + * <p>The method {@link #append(boolean, boolean)} is used.</p> + * + * @param lhs the left hand <code>boolean[]</code> + * @param rhs the right hand <code>boolean[]</code> + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(boolean[] lhs, boolean[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + * <p>Returns <code>true</code> if the fields that have been checked + * are all equal.</p> + * + * @return boolean + */ + public boolean isEquals() { + return this.isEquals; + } + + /** + * <p>Returns <code>true</code> if the fields that have been checked + * are all equal.</p> + * + * @return <code>true</code> if all of the fields that have been checked + * are equal, <code>false</code> otherwise. + * + * @since 3.0 + */ + public Boolean build() { + return Boolean.valueOf(isEquals()); + } + + /** + * Sets the <code>isEquals</code> value. + * + * @param isEquals The value to set. + * @since 2.1 + */ + protected void setEquals(boolean isEquals) { + this.isEquals = isEquals; + } + + /** + * Reset the EqualsBuilder so you can use the same object again + * @since 2.5 + */ + public void reset() { + this.isEquals = true; + } +} diff --git a/src/org/apache/commons/lang3/builder/HashCodeBuilder.java b/src/org/apache/commons/lang3/builder/HashCodeBuilder.java index 9ae2bd5..2afa483 100644 --- a/src/org/apache/commons/lang3/builder/HashCodeBuilder.java +++ b/src/org/apache/commons/lang3/builder/HashCodeBuilder.java @@ -1,961 +1,961 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.lang3.builder;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.Set;
-
-import org.apache.commons.lang3.ArrayUtils;
-
-/**
- * <p>
- * Assists in implementing {@link Object#hashCode()} methods.
- * </p>
- *
- * <p>
- * This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in
- * the book <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> by Joshua Bloch. Writing a
- * good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process.
- * </p>
- *
- * <p>
- * The following is the approach taken. When appending a data field, the current total is multiplied by the
- * multiplier then a relevant value
- * for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then
- * appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45.
- * </p>
- *
- * <p>
- * All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be
- * excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code>
- * method.
- * </p>
- *
- * <p>
- * To use this class write code as follows:
- * </p>
- *
- * <pre>
- * public class Person {
- * String name;
- * int age;
- * boolean smoker;
- * ...
- *
- * public int hashCode() {
- * // you pick a hard-coded, randomly chosen, non-zero, odd number
- * // ideally different for each class
- * return new HashCodeBuilder(17, 37).
- * append(name).
- * append(age).
- * append(smoker).
- * toHashCode();
- * }
- * }
- * </pre>
- *
- * <p>
- * If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}.
- * </p>
- *
- * <p>
- * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are
- * usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code>
- * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions
- * are set up correctly. It is also slower than testing explicitly.
- * </p>
- *
- * <p>
- * A typical invocation for this method would look like:
- * </p>
- *
- * <pre>
- * public int hashCode() {
- * return HashCodeBuilder.reflectionHashCode(this);
- * }
- * </pre>
- *
- * @since 1.0
- * @version $Id: HashCodeBuilder.java 1144929 2011-07-10 18:26:16Z ggregory $
- */
-public class HashCodeBuilder implements Builder<Integer> {
- /**
- * <p>
- * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops.
- * </p>
- *
- * @since 2.3
- */
- private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<Set<IDKey>>();
-
- /*
- * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode()
- * we are in the process of calculating.
- *
- * So we generate a one-to-one mapping from the original object to a new object.
- *
- * Now HashSet uses equals() to determine if two elements with the same hashcode really
- * are equal, so we also need to ensure that the replacement objects are only equal
- * if the original objects are identical.
- *
- * The original implementation (2.4 and before) used the System.indentityHashCode()
- * method - however this is not guaranteed to generate unique ids (e.g. LANG-459)
- *
- * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey)
- * to disambiguate the duplicate ids.
- */
-
- /**
- * <p>
- * Returns the registry of objects being traversed by the reflection methods in the current thread.
- * </p>
- *
- * @return Set the registry of objects being traversed
- * @since 2.3
- */
- static Set<IDKey> getRegistry() {
- return REGISTRY.get();
- }
-
- /**
- * <p>
- * Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid
- * infinite loops.
- * </p>
- *
- * @param value
- * The object to lookup in the registry.
- * @return boolean <code>true</code> if the registry contains the given object.
- * @since 2.3
- */
- static boolean isRegistered(Object value) {
- Set<IDKey> registry = getRegistry();
- return registry != null && registry.contains(new IDKey(value));
- }
-
- /**
- * <p>
- * Appends the fields and values defined by the given object of the given <code>Class</code>.
- * </p>
- *
- * @param object
- * the object to append details of
- * @param clazz
- * the class to append details of
- * @param builder
- * the builder to append to
- * @param useTransients
- * whether to use transient fields
- * @param excludeFields
- * Collection of String field names to exclude from use in calculation of hash code
- */
- private static void reflectionAppend(Object object, Class<?> clazz, HashCodeBuilder builder, boolean useTransients,
- String[] excludeFields) {
- if (isRegistered(object)) {
- return;
- }
- try {
- register(object);
- Field[] fields = clazz.getDeclaredFields();
- AccessibleObject.setAccessible(fields, true);
- for (Field field : fields) {
- if (!ArrayUtils.contains(excludeFields, field.getName())
- && (field.getName().indexOf('$') == -1)
- && (useTransients || !Modifier.isTransient(field.getModifiers()))
- && (!Modifier.isStatic(field.getModifiers()))) {
- try {
- Object fieldValue = field.get(object);
- builder.append(fieldValue);
- } catch (IllegalAccessException e) {
- // this can't happen. Would get a Security exception instead
- // throw a runtime exception in case the impossible happens.
- throw new InternalError("Unexpected IllegalAccessException");
- }
- }
- }
- } finally {
- unregister(object);
- }
- }
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
- * <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be tested. Superclass fields will be included.
- * </p>
- *
- * <p>
- * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
- * however this is not vital. Prime numbers are preferred, especially for the multiplier.
- * </p>
- *
- * @param initialNonZeroOddNumber
- * a non-zero, odd number used as the initial value
- * @param multiplierNonZeroOddNumber
- * a non-zero, odd number used as the multiplier
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @return int hash code
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- * @throws IllegalArgumentException
- * if the number is zero or even
- */
- public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) {
- return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null);
- }
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be tested. Superclass fields will be included.
- * </p>
- *
- * <p>
- * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
- * however this is not vital. Prime numbers are preferred, especially for the multiplier.
- * </p>
- *
- * @param initialNonZeroOddNumber
- * a non-zero, odd number used as the initial value
- * @param multiplierNonZeroOddNumber
- * a non-zero, odd number used as the multiplier
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @param testTransients
- * whether to include transient fields
- * @return int hash code
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- * @throws IllegalArgumentException
- * if the number is zero or even
- */
- public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object,
- boolean testTransients) {
- return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null);
- }
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be included. Superclass fields will be included up to and including the specified
- * superclass. A null superclass is treated as java.lang.Object.
- * </p>
- *
- * <p>
- * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
- * however this is not vital. Prime numbers are preferred, especially for the multiplier.
- * </p>
- *
- * @param <T>
- * the type of the object involved
- * @param initialNonZeroOddNumber
- * a non-zero, odd number used as the initial value
- * @param multiplierNonZeroOddNumber
- * a non-zero, odd number used as the multiplier
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @param testTransients
- * whether to include transient fields
- * @param reflectUpToClass
- * the superclass to reflect up to (inclusive), may be <code>null</code>
- * @param excludeFields
- * array of field names to exclude from use in calculation of hash code
- * @return int hash code
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- * @throws IllegalArgumentException
- * if the number is zero or even
- * @since 2.0
- */
- public static <T> int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, T object,
- boolean testTransients, Class<? super T> reflectUpToClass, String... excludeFields) {
-
- if (object == null) {
- throw new IllegalArgumentException("The object to build a hash code for must not be null");
- }
- HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber);
- Class<?> clazz = object.getClass();
- reflectionAppend(object, clazz, builder, testTransients, excludeFields);
- while (clazz.getSuperclass() != null && clazz != reflectUpToClass) {
- clazz = clazz.getSuperclass();
- reflectionAppend(object, clazz, builder, testTransients, excludeFields);
- }
- return builder.toHashCode();
- }
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * This constructor uses two hard coded choices for the constants needed to build a hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <P>
- * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be tested. Superclass fields will be included.
- * </p>
- *
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @param testTransients
- * whether to include transient fields
- * @return int hash code
- * @throws IllegalArgumentException
- * if the object is <code>null</code>
- */
- public static int reflectionHashCode(Object object, boolean testTransients) {
- return reflectionHashCode(17, 37, object, testTransients, null);
- }
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * This constructor uses two hard coded choices for the constants needed to build a hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
- * <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be tested. Superclass fields will be included.
- * </p>
- *
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @param excludeFields
- * Collection of String field names to exclude from use in calculation of hash code
- * @return int hash code
- * @throws IllegalArgumentException
- * if the object is <code>null</code>
- */
- public static int reflectionHashCode(Object object, Collection<String> excludeFields) {
- return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields));
- }
-
- // -------------------------------------------------------------------------
-
- /**
- * <p>
- * This method uses reflection to build a valid hash code.
- * </p>
- *
- * <p>
- * This constructor uses two hard coded choices for the constants needed to build a hash code.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * Transient members will be not be used, as they are likely derived fields, and not part of the value of the
- * <code>Object</code>.
- * </p>
- *
- * <p>
- * Static fields will not be tested. Superclass fields will be included.
- * </p>
- *
- * @param object
- * the Object to create a <code>hashCode</code> for
- * @param excludeFields
- * array of field names to exclude from use in calculation of hash code
- * @return int hash code
- * @throws IllegalArgumentException
- * if the object is <code>null</code>
- */
- public static int reflectionHashCode(Object object, String... excludeFields) {
- return reflectionHashCode(17, 37, object, false, null, excludeFields);
- }
-
- /**
- * <p>
- * Registers the given object. Used by the reflection methods to avoid infinite loops.
- * </p>
- *
- * @param value
- * The object to register.
- */
- static void register(Object value) {
- synchronized (HashCodeBuilder.class) {
- if (getRegistry() == null) {
- REGISTRY.set(new HashSet<IDKey>());
- }
- }
- getRegistry().add(new IDKey(value));
- }
-
- /**
- * <p>
- * Unregisters the given object.
- * </p>
- *
- * <p>
- * Used by the reflection methods to avoid infinite loops.
- *
- * @param value
- * The object to unregister.
- * @since 2.3
- */
- static void unregister(Object value) {
- Set<IDKey> registry = getRegistry();
- if (registry != null) {
- registry.remove(new IDKey(value));
- synchronized (HashCodeBuilder.class) {
- //read again
- registry = getRegistry();
- if (registry != null && registry.isEmpty()) {
- REGISTRY.remove();
- }
- }
- }
- }
-
- /**
- * Constant to use in building the hashCode.
- */
- private final int iConstant;
-
- /**
- * Running total of the hashCode.
- */
- private int iTotal = 0;
-
- /**
- * <p>
- * Uses two hard coded choices for the constants needed to build a <code>hashCode</code>.
- * </p>
- */
- public HashCodeBuilder() {
- iConstant = 37;
- iTotal = 17;
- }
-
- /**
- * <p>
- * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class,
- * however this is not vital.
- * </p>
- *
- * <p>
- * Prime numbers are preferred, especially for the multiplier.
- * </p>
- *
- * @param initialNonZeroOddNumber
- * a non-zero, odd number used as the initial value
- * @param multiplierNonZeroOddNumber
- * a non-zero, odd number used as the multiplier
- * @throws IllegalArgumentException
- * if the number is zero or even
- */
- public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) {
- if (initialNonZeroOddNumber == 0) {
- throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value");
- }
- if (initialNonZeroOddNumber % 2 == 0) {
- throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value");
- }
- if (multiplierNonZeroOddNumber == 0) {
- throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier");
- }
- if (multiplierNonZeroOddNumber % 2 == 0) {
- throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier");
- }
- iConstant = multiplierNonZeroOddNumber;
- iTotal = initialNonZeroOddNumber;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>boolean</code>.
- * </p>
- * <p>
- * This adds <code>1</code> when true, and <code>0</code> when false to the <code>hashCode</code>.
- * </p>
- * <p>
- * This is in contrast to the standard <code>java.lang.Boolean.hashCode</code> handling, which computes
- * a <code>hashCode</code> value of <code>1231</code> for <code>java.lang.Boolean</code> instances
- * that represent <code>true</code> or <code>1237</code> for <code>java.lang.Boolean</code> instances
- * that represent <code>false</code>.
- * </p>
- * <p>
- * This is in accordance with the <quote>Effective Java</quote> design.
- * </p>
- *
- * @param value
- * the boolean to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(boolean value) {
- iTotal = iTotal * iConstant + (value ? 0 : 1);
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>boolean</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(boolean[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (boolean element : array) {
- append(element);
- }
- }
- return this;
- }
-
- // -------------------------------------------------------------------------
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>byte</code>.
- * </p>
- *
- * @param value
- * the byte to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(byte value) {
- iTotal = iTotal * iConstant + value;
- return this;
- }
-
- // -------------------------------------------------------------------------
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>byte</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(byte[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (byte element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>char</code>.
- * </p>
- *
- * @param value
- * the char to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(char value) {
- iTotal = iTotal * iConstant + value;
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>char</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(char[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (char element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>double</code>.
- * </p>
- *
- * @param value
- * the double to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(double value) {
- return append(Double.doubleToLongBits(value));
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>double</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(double[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (double element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>float</code>.
- * </p>
- *
- * @param value
- * the float to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(float value) {
- iTotal = iTotal * iConstant + Float.floatToIntBits(value);
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>float</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(float[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (float element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for an <code>int</code>.
- * </p>
- *
- * @param value
- * the int to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(int value) {
- iTotal = iTotal * iConstant + value;
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for an <code>int</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(int[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (int element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>long</code>.
- * </p>
- *
- * @param value
- * the long to add to the <code>hashCode</code>
- * @return this
- */
- // NOTE: This method uses >> and not >>> as Effective Java and
- // Long.hashCode do. Ideally we should switch to >>> at
- // some stage. There are backwards compat issues, so
- // that will have to wait for the time being. cf LANG-342.
- public HashCodeBuilder append(long value) {
- iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32)));
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>long</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(long[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (long element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for an <code>Object</code>.
- * </p>
- *
- * @param object
- * the Object to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(Object object) {
- if (object == null) {
- iTotal = iTotal * iConstant;
-
- } else {
- if(object.getClass().isArray()) {
- // 'Switch' on type of array, to dispatch to the correct handler
- // This handles multi dimensional arrays
- if (object instanceof long[]) {
- append((long[]) object);
- } else if (object instanceof int[]) {
- append((int[]) object);
- } else if (object instanceof short[]) {
- append((short[]) object);
- } else if (object instanceof char[]) {
- append((char[]) object);
- } else if (object instanceof byte[]) {
- append((byte[]) object);
- } else if (object instanceof double[]) {
- append((double[]) object);
- } else if (object instanceof float[]) {
- append((float[]) object);
- } else if (object instanceof boolean[]) {
- append((boolean[]) object);
- } else {
- // Not an array of primitives
- append((Object[]) object);
- }
- } else {
- iTotal = iTotal * iConstant + object.hashCode();
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for an <code>Object</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(Object[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (Object element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>short</code>.
- * </p>
- *
- * @param value
- * the short to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(short value) {
- iTotal = iTotal * iConstant + value;
- return this;
- }
-
- /**
- * <p>
- * Append a <code>hashCode</code> for a <code>short</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>hashCode</code>
- * @return this
- */
- public HashCodeBuilder append(short[] array) {
- if (array == null) {
- iTotal = iTotal * iConstant;
- } else {
- for (short element : array) {
- append(element);
- }
- }
- return this;
- }
-
- /**
- * <p>
- * Adds the result of super.hashCode() to this builder.
- * </p>
- *
- * @param superHashCode
- * the result of calling <code>super.hashCode()</code>
- * @return this HashCodeBuilder, used to chain calls.
- * @since 2.0
- */
- public HashCodeBuilder appendSuper(int superHashCode) {
- iTotal = iTotal * iConstant + superHashCode;
- return this;
- }
-
- /**
- * <p>
- * Return the computed <code>hashCode</code>.
- * </p>
- *
- * @return <code>hashCode</code> based on the fields appended
- */
- public int toHashCode() {
- return iTotal;
- }
-
- /**
- * Returns the computed <code>hashCode</code>.
- *
- * @return <code>hashCode</code> based on the fields appended
- *
- * @since 3.0
- */
- public Integer build() {
- return Integer.valueOf(toHashCode());
- }
-
- /**
- * <p>
- * The computed <code>hashCode</code> from toHashCode() is returned due to the likelihood
- * of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for
- * HashCodeBuilder itself is.</p>
- *
- * @return <code>hashCode</code> based on the fields appended
- * @since 2.5
- */
- @Override
- public int hashCode() {
- return toHashCode();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * <p> + * Assists in implementing {@link Object#hashCode()} methods. + * </p> + * + * <p> + * This class enables a good <code>hashCode</code> method to be built for any class. It follows the rules laid out in + * the book <a href="http://java.sun.com/docs/books/effective/index.html">Effective Java</a> by Joshua Bloch. Writing a + * good <code>hashCode</code> method is actually quite difficult. This class aims to simplify the process. + * </p> + * + * <p> + * The following is the approach taken. When appending a data field, the current total is multiplied by the + * multiplier then a relevant value + * for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then + * appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45. + * </p> + * + * <p> + * All relevant fields from the object should be included in the <code>hashCode</code> method. Derived fields may be + * excluded. In general, any field used in the <code>equals</code> method must be used in the <code>hashCode</code> + * method. + * </p> + * + * <p> + * To use this class write code as follows: + * </p> + * + * <pre> + * public class Person { + * String name; + * int age; + * boolean smoker; + * ... + * + * public int hashCode() { + * // you pick a hard-coded, randomly chosen, non-zero, odd number + * // ideally different for each class + * return new HashCodeBuilder(17, 37). + * append(name). + * append(age). + * append(smoker). + * toHashCode(); + * } + * } + * </pre> + * + * <p> + * If required, the superclass <code>hashCode()</code> can be added using {@link #appendSuper}. + * </p> + * + * <p> + * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are + * usually private, the method, <code>reflectionHashCode</code>, uses <code>AccessibleObject.setAccessible</code> + * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions + * are set up correctly. It is also slower than testing explicitly. + * </p> + * + * <p> + * A typical invocation for this method would look like: + * </p> + * + * <pre> + * public int hashCode() { + * return HashCodeBuilder.reflectionHashCode(this); + * } + * </pre> + * + * @since 1.0 + * @version $Id: HashCodeBuilder.java 1144929 2011-07-10 18:26:16Z ggregory $ + */ +public class HashCodeBuilder implements Builder<Integer> { + /** + * <p> + * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops. + * </p> + * + * @since 2.3 + */ + private static final ThreadLocal<Set<IDKey>> REGISTRY = new ThreadLocal<Set<IDKey>>(); + + /* + * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode() + * we are in the process of calculating. + * + * So we generate a one-to-one mapping from the original object to a new object. + * + * Now HashSet uses equals() to determine if two elements with the same hashcode really + * are equal, so we also need to ensure that the replacement objects are only equal + * if the original objects are identical. + * + * The original implementation (2.4 and before) used the System.indentityHashCode() + * method - however this is not guaranteed to generate unique ids (e.g. LANG-459) + * + * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey) + * to disambiguate the duplicate ids. + */ + + /** + * <p> + * Returns the registry of objects being traversed by the reflection methods in the current thread. + * </p> + * + * @return Set the registry of objects being traversed + * @since 2.3 + */ + static Set<IDKey> getRegistry() { + return REGISTRY.get(); + } + + /** + * <p> + * Returns <code>true</code> if the registry contains the given object. Used by the reflection methods to avoid + * infinite loops. + * </p> + * + * @param value + * The object to lookup in the registry. + * @return boolean <code>true</code> if the registry contains the given object. + * @since 2.3 + */ + static boolean isRegistered(Object value) { + Set<IDKey> registry = getRegistry(); + return registry != null && registry.contains(new IDKey(value)); + } + + /** + * <p> + * Appends the fields and values defined by the given object of the given <code>Class</code>. + * </p> + * + * @param object + * the object to append details of + * @param clazz + * the class to append details of + * @param builder + * the builder to append to + * @param useTransients + * whether to use transient fields + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code + */ + private static void reflectionAppend(Object object, Class<?> clazz, HashCodeBuilder builder, boolean useTransients, + String[] excludeFields) { + if (isRegistered(object)) { + return; + } + try { + register(object); + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field field : fields) { + if (!ArrayUtils.contains(excludeFields, field.getName()) + && (field.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(field.getModifiers())) + && (!Modifier.isStatic(field.getModifiers()))) { + try { + Object fieldValue = field.get(object); + builder.append(fieldValue); + } catch (IllegalAccessException e) { + // this can't happen. Would get a Security exception instead + // throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } finally { + unregister(object); + } + } + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be tested. Superclass fields will be included. + * </p> + * + * <p> + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + * </p> + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a <code>hashCode</code> for + * @return int hash code + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * @throws IllegalArgumentException + * if the number is zero or even + */ + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null); + } + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be tested. Superclass fields will be included. + * </p> + * + * <p> + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + * </p> + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a <code>hashCode</code> for + * @param testTransients + * whether to include transient fields + * @return int hash code + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * @throws IllegalArgumentException + * if the number is zero or even + */ + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object, + boolean testTransients) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null); + } + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be included. Superclass fields will be included up to and including the specified + * superclass. A null superclass is treated as java.lang.Object. + * </p> + * + * <p> + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + * </p> + * + * @param <T> + * the type of the object involved + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a <code>hashCode</code> for + * @param testTransients + * whether to include transient fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be <code>null</code> + * @param excludeFields + * array of field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * @throws IllegalArgumentException + * if the number is zero or even + * @since 2.0 + */ + public static <T> int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, T object, + boolean testTransients, Class<? super T> reflectUpToClass, String... excludeFields) { + + if (object == null) { + throw new IllegalArgumentException("The object to build a hash code for must not be null"); + } + HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber); + Class<?> clazz = object.getClass(); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); + while (clazz.getSuperclass() != null && clazz != reflectUpToClass) { + clazz = clazz.getSuperclass(); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); + } + return builder.toHashCode(); + } + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * This constructor uses two hard coded choices for the constants needed to build a hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <P> + * If the TestTransients parameter is set to <code>true</code>, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be tested. Superclass fields will be included. + * </p> + * + * @param object + * the Object to create a <code>hashCode</code> for + * @param testTransients + * whether to include transient fields + * @return int hash code + * @throws IllegalArgumentException + * if the object is <code>null</code> + */ + public static int reflectionHashCode(Object object, boolean testTransients) { + return reflectionHashCode(17, 37, object, testTransients, null); + } + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * This constructor uses two hard coded choices for the constants needed to build a hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be tested. Superclass fields will be included. + * </p> + * + * @param object + * the Object to create a <code>hashCode</code> for + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is <code>null</code> + */ + public static int reflectionHashCode(Object object, Collection<String> excludeFields) { + return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + // ------------------------------------------------------------------------- + + /** + * <p> + * This method uses reflection to build a valid hash code. + * </p> + * + * <p> + * This constructor uses two hard coded choices for the constants needed to build a hash code. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * <code>Object</code>. + * </p> + * + * <p> + * Static fields will not be tested. Superclass fields will be included. + * </p> + * + * @param object + * the Object to create a <code>hashCode</code> for + * @param excludeFields + * array of field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is <code>null</code> + */ + public static int reflectionHashCode(Object object, String... excludeFields) { + return reflectionHashCode(17, 37, object, false, null, excludeFields); + } + + /** + * <p> + * Registers the given object. Used by the reflection methods to avoid infinite loops. + * </p> + * + * @param value + * The object to register. + */ + static void register(Object value) { + synchronized (HashCodeBuilder.class) { + if (getRegistry() == null) { + REGISTRY.set(new HashSet<IDKey>()); + } + } + getRegistry().add(new IDKey(value)); + } + + /** + * <p> + * Unregisters the given object. + * </p> + * + * <p> + * Used by the reflection methods to avoid infinite loops. + * + * @param value + * The object to unregister. + * @since 2.3 + */ + static void unregister(Object value) { + Set<IDKey> registry = getRegistry(); + if (registry != null) { + registry.remove(new IDKey(value)); + synchronized (HashCodeBuilder.class) { + //read again + registry = getRegistry(); + if (registry != null && registry.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * Constant to use in building the hashCode. + */ + private final int iConstant; + + /** + * Running total of the hashCode. + */ + private int iTotal = 0; + + /** + * <p> + * Uses two hard coded choices for the constants needed to build a <code>hashCode</code>. + * </p> + */ + public HashCodeBuilder() { + iConstant = 37; + iTotal = 17; + } + + /** + * <p> + * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. + * </p> + * + * <p> + * Prime numbers are preferred, especially for the multiplier. + * </p> + * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @throws IllegalArgumentException + * if the number is zero or even + */ + public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { + if (initialNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); + } + if (initialNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); + } + if (multiplierNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); + } + if (multiplierNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); + } + iConstant = multiplierNonZeroOddNumber; + iTotal = initialNonZeroOddNumber; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>boolean</code>. + * </p> + * <p> + * This adds <code>1</code> when true, and <code>0</code> when false to the <code>hashCode</code>. + * </p> + * <p> + * This is in contrast to the standard <code>java.lang.Boolean.hashCode</code> handling, which computes + * a <code>hashCode</code> value of <code>1231</code> for <code>java.lang.Boolean</code> instances + * that represent <code>true</code> or <code>1237</code> for <code>java.lang.Boolean</code> instances + * that represent <code>false</code>. + * </p> + * <p> + * This is in accordance with the <quote>Effective Java</quote> design. + * </p> + * + * @param value + * the boolean to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(boolean value) { + iTotal = iTotal * iConstant + (value ? 0 : 1); + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>boolean</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(boolean[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (boolean element : array) { + append(element); + } + } + return this; + } + + // ------------------------------------------------------------------------- + + /** + * <p> + * Append a <code>hashCode</code> for a <code>byte</code>. + * </p> + * + * @param value + * the byte to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(byte value) { + iTotal = iTotal * iConstant + value; + return this; + } + + // ------------------------------------------------------------------------- + + /** + * <p> + * Append a <code>hashCode</code> for a <code>byte</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(byte[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (byte element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>char</code>. + * </p> + * + * @param value + * the char to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(char value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>char</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(char[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (char element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>double</code>. + * </p> + * + * @param value + * the double to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(double value) { + return append(Double.doubleToLongBits(value)); + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>double</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(double[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (double element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>float</code>. + * </p> + * + * @param value + * the float to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(float value) { + iTotal = iTotal * iConstant + Float.floatToIntBits(value); + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>float</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(float[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (float element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for an <code>int</code>. + * </p> + * + * @param value + * the int to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(int value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for an <code>int</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(int[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (int element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>long</code>. + * </p> + * + * @param value + * the long to add to the <code>hashCode</code> + * @return this + */ + // NOTE: This method uses >> and not >>> as Effective Java and + // Long.hashCode do. Ideally we should switch to >>> at + // some stage. There are backwards compat issues, so + // that will have to wait for the time being. cf LANG-342. + public HashCodeBuilder append(long value) { + iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32))); + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>long</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(long[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (long element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for an <code>Object</code>. + * </p> + * + * @param object + * the Object to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(Object object) { + if (object == null) { + iTotal = iTotal * iConstant; + + } else { + if(object.getClass().isArray()) { + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays + if (object instanceof long[]) { + append((long[]) object); + } else if (object instanceof int[]) { + append((int[]) object); + } else if (object instanceof short[]) { + append((short[]) object); + } else if (object instanceof char[]) { + append((char[]) object); + } else if (object instanceof byte[]) { + append((byte[]) object); + } else if (object instanceof double[]) { + append((double[]) object); + } else if (object instanceof float[]) { + append((float[]) object); + } else if (object instanceof boolean[]) { + append((boolean[]) object); + } else { + // Not an array of primitives + append((Object[]) object); + } + } else { + iTotal = iTotal * iConstant + object.hashCode(); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for an <code>Object</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(Object[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (Object element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>short</code>. + * </p> + * + * @param value + * the short to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(short value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + * <p> + * Append a <code>hashCode</code> for a <code>short</code> array. + * </p> + * + * @param array + * the array to add to the <code>hashCode</code> + * @return this + */ + public HashCodeBuilder append(short[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (short element : array) { + append(element); + } + } + return this; + } + + /** + * <p> + * Adds the result of super.hashCode() to this builder. + * </p> + * + * @param superHashCode + * the result of calling <code>super.hashCode()</code> + * @return this HashCodeBuilder, used to chain calls. + * @since 2.0 + */ + public HashCodeBuilder appendSuper(int superHashCode) { + iTotal = iTotal * iConstant + superHashCode; + return this; + } + + /** + * <p> + * Return the computed <code>hashCode</code>. + * </p> + * + * @return <code>hashCode</code> based on the fields appended + */ + public int toHashCode() { + return iTotal; + } + + /** + * Returns the computed <code>hashCode</code>. + * + * @return <code>hashCode</code> based on the fields appended + * + * @since 3.0 + */ + public Integer build() { + return Integer.valueOf(toHashCode()); + } + + /** + * <p> + * The computed <code>hashCode</code> from toHashCode() is returned due to the likelihood + * of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for + * HashCodeBuilder itself is.</p> + * + * @return <code>hashCode</code> based on the fields appended + * @since 2.5 + */ + @Override + public int hashCode() { + return toHashCode(); + } + +} diff --git a/src/org/apache/commons/lang3/builder/IDKey.java b/src/org/apache/commons/lang3/builder/IDKey.java index 68414c0..6b1d4ee 100644 --- a/src/org/apache/commons/lang3/builder/IDKey.java +++ b/src/org/apache/commons/lang3/builder/IDKey.java @@ -1,74 +1,74 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.apache.commons.lang3.builder;
-
-// adapted from org.apache.axis.utils.IDKey
-
-/**
- * Wrap an identity key (System.identityHashCode())
- * so that an object can only be equal() to itself.
- *
- * This is necessary to disambiguate the occasional duplicate
- * identityHashCodes that can occur.
- *
- */
-final class IDKey {
- private final Object value;
- private final int id;
-
- /**
- * Constructor for IDKey
- * @param _value The value
- */
- public IDKey(Object _value) {
- // This is the Object hashcode
- id = System.identityHashCode(_value);
- // There have been some cases (LANG-459) that return the
- // same identity hash code for different objects. So
- // the value is also added to disambiguate these cases.
- value = _value;
- }
-
- /**
- * returns hashcode - i.e. the system identity hashcode.
- * @return the hashcode
- */
- @Override
- public int hashCode() {
- return id;
- }
-
- /**
- * checks if instances are equal
- * @param other The other object to compare to
- * @return if the instances are for the same object
- */
- @Override
- public boolean equals(Object other) {
- if (!(other instanceof IDKey)) {
- return false;
- }
- IDKey idKey = (IDKey) other;
- if (id != idKey.id) {
- return false;
- }
- // Note that identity equals is used.
- return value == idKey.value;
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.commons.lang3.builder; + +// adapted from org.apache.axis.utils.IDKey + +/** + * Wrap an identity key (System.identityHashCode()) + * so that an object can only be equal() to itself. + * + * This is necessary to disambiguate the occasional duplicate + * identityHashCodes that can occur. + * + */ +final class IDKey { + private final Object value; + private final int id; + + /** + * Constructor for IDKey + * @param _value The value + */ + public IDKey(Object _value) { + // This is the Object hashcode + id = System.identityHashCode(_value); + // There have been some cases (LANG-459) that return the + // same identity hash code for different objects. So + // the value is also added to disambiguate these cases. + value = _value; + } + + /** + * returns hashcode - i.e. the system identity hashcode. + * @return the hashcode + */ + @Override + public int hashCode() { + return id; + } + + /** + * checks if instances are equal + * @param other The other object to compare to + * @return if the instances are for the same object + */ + @Override + public boolean equals(Object other) { + if (!(other instanceof IDKey)) { + return false; + } + IDKey idKey = (IDKey) other; + if (id != idKey.id) { + return false; + } + // Note that identity equals is used. + return value == idKey.value; + } +} diff --git a/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java index df85657..a7ecbac 100644 --- a/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java +++ b/src/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java @@ -1,697 +1,697 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.lang3.builder;
-
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ClassUtils;
-
-/**
- * <p>
- * Assists in implementing {@link Object#toString()} methods using reflection.
- * </p>
- *
- * <p>
- * This class uses reflection to determine the fields to append. Because these fields are usually private, the class
- * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to
- * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are
- * set up correctly.
- * </p>
- *
- * <p>
- * A typical invocation for this method would look like:
- * </p>
- *
- * <pre>
- * public String toString() {
- * return ReflectionToStringBuilder.toString(this);
- * }</pre>
- *
- *
- *
- * <p>
- * You can also use the builder to debug 3rd party objects:
- * </p>
- *
- * <pre>
- * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));</pre>
- *
- *
- *
- * <p>
- * A subclass can control field output by overriding the methods:
- * <ul>
- * <li>{@link #accept(java.lang.reflect.Field)}</li>
- * <li>{@link #getValue(java.lang.reflect.Field)}</li>
- * </ul>
- * </p>
- * <p>
- * For example, this method does <i>not</i> include the <code>password</code> field in the returned
- * <code>String</code>:
- * </p>
- *
- * <pre>
- * public String toString() {
- * return (new ReflectionToStringBuilder(this) {
- * protected boolean accept(Field f) {
- * return super.accept(f) && !f.getName().equals("password");
- * }
- * }).toString();
- * }</pre>
- *
- *
- *
- * <p>
- * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the
- * constructor.
- * </p>
- *
- * @since 2.0
- * @version $Id: ReflectionToStringBuilder.java 1090821 2011-04-10 15:59:07Z mbenson $
- */
-public class ReflectionToStringBuilder extends ToStringBuilder {
-
- /**
- * <p>
- * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * Transient members will be not be included, as they are likely derived. Static fields will not be included.
- * Superclass fields will be appended.
- * </p>
- *
- * @param object
- * the Object to be output
- * @return the String result
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- */
- public static String toString(Object object) {
- return toString(object, null, false, false, null);
- }
-
- /**
- * <p>
- * Builds a <code>toString</code> value through reflection.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * Transient members will be not be included, as they are likely derived. Static fields will not be included.
- * Superclass fields will be appended.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
- * </p>
- *
- * @param object
- * the Object to be output
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @return the String result
- * @throws IllegalArgumentException
- * if the Object or <code>ToStringStyle</code> is <code>null</code>
- */
- public static String toString(Object object, ToStringStyle style) {
- return toString(object, style, false, false, null);
- }
-
- /**
- * <p>
- * Builds a <code>toString</code> value through reflection.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * If the <code>outputTransients</code> is <code>true</code>, transient members will be output, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the Object.
- * </p>
- *
- * <p>
- * Static fields will not be included. Superclass fields will be appended.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
- * </p>
- *
- * @param object
- * the Object to be output
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @param outputTransients
- * whether to include transient fields
- * @return the String result
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- */
- public static String toString(Object object, ToStringStyle style, boolean outputTransients) {
- return toString(object, style, outputTransients, false, null);
- }
-
- /**
- * <p>
- * Builds a <code>toString</code> value through reflection.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the Object.
- * </p>
- *
- * <p>
- * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
- * ignored.
- * </p>
- *
- * <p>
- * Static fields will not be included. Superclass fields will be appended.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
- * </p>
- *
- * @param object
- * the Object to be output
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @param outputTransients
- * whether to include transient fields
- * @param outputStatics
- * whether to include transient fields
- * @return the String result
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- * @since 2.1
- */
- public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) {
- return toString(object, style, outputTransients, outputStatics, null);
- }
-
- /**
- * <p>
- * Builds a <code>toString</code> value through reflection.
- * </p>
- *
- * <p>
- * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will
- * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is
- * also not as efficient as testing explicitly.
- * </p>
- *
- * <p>
- * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they
- * are ignored, as they are likely derived fields, and not part of the value of the Object.
- * </p>
- *
- * <p>
- * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are
- * ignored.
- * </p>
- *
- * <p>
- * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as
- * <code>java.lang.Object</code>.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used.
- * </p>
- *
- * @param <T>
- * the type of the object
- * @param object
- * the Object to be output
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @param outputTransients
- * whether to include transient fields
- * @param outputStatics
- * whether to include static fields
- * @param reflectUpToClass
- * the superclass to reflect up to (inclusive), may be <code>null</code>
- * @return the String result
- * @throws IllegalArgumentException
- * if the Object is <code>null</code>
- * @since 2.1
- */
- public static <T> String toString(
- T object, ToStringStyle style, boolean outputTransients,
- boolean outputStatics, Class<? super T> reflectUpToClass) {
- return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics)
- .toString();
- }
-
- /**
- * Builds a String for a toString method excluding the given field names.
- *
- * @param object
- * The object to "toString".
- * @param excludeFieldNames
- * The field names to exclude. Null excludes nothing.
- * @return The toString value.
- */
- public static String toStringExclude(Object object, Collection<String> excludeFieldNames) {
- return toStringExclude(object, toNoNullStringArray(excludeFieldNames));
- }
-
- /**
- * Converts the given Collection into an array of Strings. The returned array does not contain <code>null</code>
- * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element
- * is <code>null</code>.
- *
- * @param collection
- * The collection to convert
- * @return A new array of Strings.
- */
- static String[] toNoNullStringArray(Collection<String> collection) {
- if (collection == null) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- return toNoNullStringArray(collection.toArray());
- }
-
- /**
- * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists
- * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException}
- * if an array element is <code>null</code>.
- *
- * @param array
- * The array to check
- * @return The given array or a new array without null.
- */
- static String[] toNoNullStringArray(Object[] array) {
- List<String> list = new ArrayList<String>(array.length);
- for (Object e : array) {
- if (e != null) {
- list.add(e.toString());
- }
- }
- return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY);
- }
-
-
- /**
- * Builds a String for a toString method excluding the given field names.
- *
- * @param object
- * The object to "toString".
- * @param excludeFieldNames
- * The field names to exclude
- * @return The toString value.
- */
- public static String toStringExclude(Object object, String... excludeFieldNames) {
- return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString();
- }
-
- /**
- * Whether or not to append static fields.
- */
- private boolean appendStatics = false;
-
- /**
- * Whether or not to append transient fields.
- */
- private boolean appendTransients = false;
-
- /**
- * Which field names to exclude from output. Intended for fields like <code>"password"</code>.
- *
- * @since 3.0 this is protected instead of private
- */
- protected String[] excludeFieldNames;
-
- /**
- * The last super class to stop appending fields for.
- */
- private Class<?> upToClass = null;
-
- /**
- * <p>
- * Constructor.
- * </p>
- *
- * <p>
- * This constructor outputs using the default style set with <code>setDefaultStyle</code>.
- * </p>
- *
- * @param object
- * the Object to build a <code>toString</code> for, must not be <code>null</code>
- * @throws IllegalArgumentException
- * if the Object passed in is <code>null</code>
- */
- public ReflectionToStringBuilder(Object object) {
- super(object);
- }
-
- /**
- * <p>
- * Constructor.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default style is used.
- * </p>
- *
- * @param object
- * the Object to build a <code>toString</code> for, must not be <code>null</code>
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @throws IllegalArgumentException
- * if the Object passed in is <code>null</code>
- */
- public ReflectionToStringBuilder(Object object, ToStringStyle style) {
- super(object, style);
- }
-
- /**
- * <p>
- * Constructor.
- * </p>
- *
- * <p>
- * If the style is <code>null</code>, the default style is used.
- * </p>
- *
- * <p>
- * If the buffer is <code>null</code>, a new one is created.
- * </p>
- *
- * @param object
- * the Object to build a <code>toString</code> for
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @param buffer
- * the <code>StringBuffer</code> to populate, may be <code>null</code>
- * @throws IllegalArgumentException
- * if the Object passed in is <code>null</code>
- */
- public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
- super(object, style, buffer);
- }
-
- /**
- * Constructor.
- *
- * @param <T>
- * the type of the object
- * @param object
- * the Object to build a <code>toString</code> for
- * @param style
- * the style of the <code>toString</code> to create, may be <code>null</code>
- * @param buffer
- * the <code>StringBuffer</code> to populate, may be <code>null</code>
- * @param reflectUpToClass
- * the superclass to reflect up to (inclusive), may be <code>null</code>
- * @param outputTransients
- * whether to include transient fields
- * @param outputStatics
- * whether to include static fields
- * @since 2.1
- */
- public <T> ReflectionToStringBuilder(
- T object, ToStringStyle style, StringBuffer buffer,
- Class<? super T> reflectUpToClass, boolean outputTransients, boolean outputStatics) {
- super(object, style, buffer);
- this.setUpToClass(reflectUpToClass);
- this.setAppendTransients(outputTransients);
- this.setAppendStatics(outputStatics);
- }
-
- /**
- * Returns whether or not to append the given <code>Field</code>.
- * <ul>
- * <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>.
- * <li>Static fields are appended only if {@link #isAppendStatics()} returns <code>true</code>.
- * <li>Inner class fields are not appened.</li>
- * </ul>
- *
- * @param field
- * The Field to test.
- * @return Whether or not to append the given <code>Field</code>.
- */
- protected boolean accept(Field field) {
- if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) {
- // Reject field from inner class.
- return false;
- }
- if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) {
- // Reject transient fields.
- return false;
- }
- if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) {
- // Reject static fields.
- return false;
- }
- if (this.excludeFieldNames != null
- && Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) {
- // Reject fields from the getExcludeFieldNames list.
- return false;
- }
- return true;
- }
-
- /**
- * <p>
- * Appends the fields and values defined by the given object of the given Class.
- * </p>
- *
- * <p>
- * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if
- * <code>Object.toString()</code> had been called and not implemented by the object.
- * </p>
- *
- * @param clazz
- * The class of object parameter
- */
- protected void appendFieldsIn(Class<?> clazz) {
- if (clazz.isArray()) {
- this.reflectionAppendArray(this.getObject());
- return;
- }
- Field[] fields = clazz.getDeclaredFields();
- AccessibleObject.setAccessible(fields, true);
- for (Field field : fields) {
- String fieldName = field.getName();
- if (this.accept(field)) {
- try {
- // Warning: Field.get(Object) creates wrappers objects
- // for primitive types.
- Object fieldValue = this.getValue(field);
- this.append(fieldName, fieldValue);
- } catch (IllegalAccessException ex) {
- //this can't happen. Would get a Security exception
- // instead
- //throw a runtime exception in case the impossible
- // happens.
- throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage());
- }
- }
- }
- }
-
- /**
- * @return Returns the excludeFieldNames.
- */
- public String[] getExcludeFieldNames() {
- return this.excludeFieldNames.clone();
- }
-
- /**
- * <p>
- * Gets the last super class to stop appending fields for.
- * </p>
- *
- * @return The last super class to stop appending fields for.
- */
- public Class<?> getUpToClass() {
- return this.upToClass;
- }
-
- /**
- * <p>
- * Calls <code>java.lang.reflect.Field.get(Object)</code>.
- * </p>
- *
- * @param field
- * The Field to query.
- * @return The Object from the given Field.
- *
- * @throws IllegalArgumentException
- * see {@link java.lang.reflect.Field#get(Object)}
- * @throws IllegalAccessException
- * see {@link java.lang.reflect.Field#get(Object)}
- *
- * @see java.lang.reflect.Field#get(Object)
- */
- protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException {
- return field.get(this.getObject());
- }
-
- /**
- * <p>
- * Gets whether or not to append static fields.
- * </p>
- *
- * @return Whether or not to append static fields.
- * @since 2.1
- */
- public boolean isAppendStatics() {
- return this.appendStatics;
- }
-
- /**
- * <p>
- * Gets whether or not to append transient fields.
- * </p>
- *
- * @return Whether or not to append transient fields.
- */
- public boolean isAppendTransients() {
- return this.appendTransients;
- }
-
- /**
- * <p>
- * Append to the <code>toString</code> an <code>Object</code> array.
- * </p>
- *
- * @param array
- * the array to add to the <code>toString</code>
- * @return this
- */
- public ReflectionToStringBuilder reflectionAppendArray(Object array) {
- this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array);
- return this;
- }
-
- /**
- * <p>
- * Sets whether or not to append static fields.
- * </p>
- *
- * @param appendStatics
- * Whether or not to append static fields.
- * @since 2.1
- */
- public void setAppendStatics(boolean appendStatics) {
- this.appendStatics = appendStatics;
- }
-
- /**
- * <p>
- * Sets whether or not to append transient fields.
- * </p>
- *
- * @param appendTransients
- * Whether or not to append transient fields.
- */
- public void setAppendTransients(boolean appendTransients) {
- this.appendTransients = appendTransients;
- }
-
- /**
- * Sets the field names to exclude.
- *
- * @param excludeFieldNamesParam
- * The excludeFieldNames to excluding from toString or <code>null</code>.
- * @return <code>this</code>
- */
- public ReflectionToStringBuilder setExcludeFieldNames(String... excludeFieldNamesParam) {
- if (excludeFieldNamesParam == null) {
- this.excludeFieldNames = null;
- } else {
- //clone and remove nulls
- this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam);
- Arrays.sort(this.excludeFieldNames);
- }
- return this;
- }
-
- /**
- * <p>
- * Sets the last super class to stop appending fields for.
- * </p>
- *
- * @param clazz
- * The last super class to stop appending fields for.
- */
- public void setUpToClass(Class<?> clazz) {
- if (clazz != null) {
- Object object = getObject();
- if (object != null && clazz.isInstance(object) == false) {
- throw new IllegalArgumentException("Specified class is not a superclass of the object");
- }
- }
- this.upToClass = clazz;
- }
-
- /**
- * <p>
- * Gets the String built by this builder.
- * </p>
- *
- * @return the built string
- */
- @Override
- public String toString() {
- if (this.getObject() == null) {
- return this.getStyle().getNullText();
- }
- Class<?> clazz = this.getObject().getClass();
- this.appendFieldsIn(clazz);
- while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) {
- clazz = clazz.getSuperclass();
- this.appendFieldsIn(clazz);
- }
- return super.toString();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; + +/** + * <p> + * Assists in implementing {@link Object#toString()} methods using reflection. + * </p> + * + * <p> + * This class uses reflection to determine the fields to append. Because these fields are usually private, the class + * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to + * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are + * set up correctly. + * </p> + * + * <p> + * A typical invocation for this method would look like: + * </p> + * + * <pre> + * public String toString() { + * return ReflectionToStringBuilder.toString(this); + * }</pre> + * + * + * + * <p> + * You can also use the builder to debug 3rd party objects: + * </p> + * + * <pre> + * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));</pre> + * + * + * + * <p> + * A subclass can control field output by overriding the methods: + * <ul> + * <li>{@link #accept(java.lang.reflect.Field)}</li> + * <li>{@link #getValue(java.lang.reflect.Field)}</li> + * </ul> + * </p> + * <p> + * For example, this method does <i>not</i> include the <code>password</code> field in the returned + * <code>String</code>: + * </p> + * + * <pre> + * public String toString() { + * return (new ReflectionToStringBuilder(this) { + * protected boolean accept(Field f) { + * return super.accept(f) && !f.getName().equals("password"); + * } + * }).toString(); + * }</pre> + * + * + * + * <p> + * The exact format of the <code>toString</code> is determined by the {@link ToStringStyle} passed into the + * constructor. + * </p> + * + * @since 2.0 + * @version $Id: ReflectionToStringBuilder.java 1090821 2011-04-10 15:59:07Z mbenson $ + */ +public class ReflectionToStringBuilder extends ToStringBuilder { + + /** + * <p> + * Builds a <code>toString</code> value using the default <code>ToStringStyle</code> through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + * </p> + * + * @param object + * the Object to be output + * @return the String result + * @throws IllegalArgumentException + * if the Object is <code>null</code> + */ + public static String toString(Object object) { + return toString(object, null, false, false, null); + } + + /** + * <p> + * Builds a <code>toString</code> value through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + * </p> + * + * <p> + * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @return the String result + * @throws IllegalArgumentException + * if the Object or <code>ToStringStyle</code> is <code>null</code> + */ + public static String toString(Object object, ToStringStyle style) { + return toString(object, style, false, false, null); + } + + /** + * <p> + * Builds a <code>toString</code> value through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the <code>outputTransients</code> is <code>true</code>, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + * </p> + * + * <p> + * Static fields will not be included. Superclass fields will be appended. + * </p> + * + * <p> + * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is <code>null</code> + */ + public static String toString(Object object, ToStringStyle style, boolean outputTransients) { + return toString(object, style, outputTransients, false, null); + } + + /** + * <p> + * Builds a <code>toString</code> value through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + * </p> + * + * <p> + * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Static fields will not be included. Superclass fields will be appended. + * </p> + * + * <p> + * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used. + * </p> + * + * @param object + * the Object to be output + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * @since 2.1 + */ + public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) { + return toString(object, style, outputTransients, outputStatics, null); + } + + /** + * <p> + * Builds a <code>toString</code> value through reflection. + * </p> + * + * <p> + * It uses <code>AccessibleObject.setAccessible</code> to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + * </p> + * + * <p> + * If the <code>outputTransients</code> is <code>true</code>, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + * </p> + * + * <p> + * If the <code>outputStatics</code> is <code>true</code>, static fields will be output, otherwise they are + * ignored. + * </p> + * + * <p> + * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * <code>java.lang.Object</code>. + * </p> + * + * <p> + * If the style is <code>null</code>, the default <code>ToStringStyle</code> is used. + * </p> + * + * @param <T> + * the type of the object + * @param object + * the Object to be output + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be <code>null</code> + * @return the String result + * @throws IllegalArgumentException + * if the Object is <code>null</code> + * @since 2.1 + */ + public static <T> String toString( + T object, ToStringStyle style, boolean outputTransients, + boolean outputStatics, Class<? super T> reflectUpToClass) { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) + .toString(); + } + + /** + * Builds a String for a toString method excluding the given field names. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude. Null excludes nothing. + * @return The toString value. + */ + public static String toStringExclude(Object object, Collection<String> excludeFieldNames) { + return toStringExclude(object, toNoNullStringArray(excludeFieldNames)); + } + + /** + * Converts the given Collection into an array of Strings. The returned array does not contain <code>null</code> + * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element + * is <code>null</code>. + * + * @param collection + * The collection to convert + * @return A new array of Strings. + */ + static String[] toNoNullStringArray(Collection<String> collection) { + if (collection == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return toNoNullStringArray(collection.toArray()); + } + + /** + * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists + * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} + * if an array element is <code>null</code>. + * + * @param array + * The array to check + * @return The given array or a new array without null. + */ + static String[] toNoNullStringArray(Object[] array) { + List<String> list = new ArrayList<String>(array.length); + for (Object e : array) { + if (e != null) { + list.add(e.toString()); + } + } + return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + + /** + * Builds a String for a toString method excluding the given field names. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude + * @return The toString value. + */ + public static String toStringExclude(Object object, String... excludeFieldNames) { + return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString(); + } + + /** + * Whether or not to append static fields. + */ + private boolean appendStatics = false; + + /** + * Whether or not to append transient fields. + */ + private boolean appendTransients = false; + + /** + * Which field names to exclude from output. Intended for fields like <code>"password"</code>. + * + * @since 3.0 this is protected instead of private + */ + protected String[] excludeFieldNames; + + /** + * The last super class to stop appending fields for. + */ + private Class<?> upToClass = null; + + /** + * <p> + * Constructor. + * </p> + * + * <p> + * This constructor outputs using the default style set with <code>setDefaultStyle</code>. + * </p> + * + * @param object + * the Object to build a <code>toString</code> for, must not be <code>null</code> + * @throws IllegalArgumentException + * if the Object passed in is <code>null</code> + */ + public ReflectionToStringBuilder(Object object) { + super(object); + } + + /** + * <p> + * Constructor. + * </p> + * + * <p> + * If the style is <code>null</code>, the default style is used. + * </p> + * + * @param object + * the Object to build a <code>toString</code> for, must not be <code>null</code> + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @throws IllegalArgumentException + * if the Object passed in is <code>null</code> + */ + public ReflectionToStringBuilder(Object object, ToStringStyle style) { + super(object, style); + } + + /** + * <p> + * Constructor. + * </p> + * + * <p> + * If the style is <code>null</code>, the default style is used. + * </p> + * + * <p> + * If the buffer is <code>null</code>, a new one is created. + * </p> + * + * @param object + * the Object to build a <code>toString</code> for + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param buffer + * the <code>StringBuffer</code> to populate, may be <code>null</code> + * @throws IllegalArgumentException + * if the Object passed in is <code>null</code> + */ + public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { + super(object, style, buffer); + } + + /** + * Constructor. + * + * @param <T> + * the type of the object + * @param object + * the Object to build a <code>toString</code> for + * @param style + * the style of the <code>toString</code> to create, may be <code>null</code> + * @param buffer + * the <code>StringBuffer</code> to populate, may be <code>null</code> + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be <code>null</code> + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @since 2.1 + */ + public <T> ReflectionToStringBuilder( + T object, ToStringStyle style, StringBuffer buffer, + Class<? super T> reflectUpToClass, boolean outputTransients, boolean outputStatics) { + super(object, style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + } + + /** + * Returns whether or not to append the given <code>Field</code>. + * <ul> + * <li>Transient fields are appended only if {@link #isAppendTransients()} returns <code>true</code>. + * <li>Static fields are appended only if {@link #isAppendStatics()} returns <code>true</code>. + * <li>Inner class fields are not appened.</li> + * </ul> + * + * @param field + * The Field to test. + * @return Whether or not to append the given <code>Field</code>. + */ + protected boolean accept(Field field) { + if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) { + // Reject field from inner class. + return false; + } + if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) { + // Reject transient fields. + return false; + } + if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) { + // Reject static fields. + return false; + } + if (this.excludeFieldNames != null + && Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) { + // Reject fields from the getExcludeFieldNames list. + return false; + } + return true; + } + + /** + * <p> + * Appends the fields and values defined by the given object of the given Class. + * </p> + * + * <p> + * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if + * <code>Object.toString()</code> had been called and not implemented by the object. + * </p> + * + * @param clazz + * The class of object parameter + */ + protected void appendFieldsIn(Class<?> clazz) { + if (clazz.isArray()) { + this.reflectionAppendArray(this.getObject()); + return; + } + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field field : fields) { + String fieldName = field.getName(); + if (this.accept(field)) { + try { + // Warning: Field.get(Object) creates wrappers objects + // for primitive types. + Object fieldValue = this.getValue(field); + this.append(fieldName, fieldValue); + } catch (IllegalAccessException ex) { + //this can't happen. Would get a Security exception + // instead + //throw a runtime exception in case the impossible + // happens. + throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage()); + } + } + } + } + + /** + * @return Returns the excludeFieldNames. + */ + public String[] getExcludeFieldNames() { + return this.excludeFieldNames.clone(); + } + + /** + * <p> + * Gets the last super class to stop appending fields for. + * </p> + * + * @return The last super class to stop appending fields for. + */ + public Class<?> getUpToClass() { + return this.upToClass; + } + + /** + * <p> + * Calls <code>java.lang.reflect.Field.get(Object)</code>. + * </p> + * + * @param field + * The Field to query. + * @return The Object from the given Field. + * + * @throws IllegalArgumentException + * see {@link java.lang.reflect.Field#get(Object)} + * @throws IllegalAccessException + * see {@link java.lang.reflect.Field#get(Object)} + * + * @see java.lang.reflect.Field#get(Object) + */ + protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException { + return field.get(this.getObject()); + } + + /** + * <p> + * Gets whether or not to append static fields. + * </p> + * + * @return Whether or not to append static fields. + * @since 2.1 + */ + public boolean isAppendStatics() { + return this.appendStatics; + } + + /** + * <p> + * Gets whether or not to append transient fields. + * </p> + * + * @return Whether or not to append transient fields. + */ + public boolean isAppendTransients() { + return this.appendTransients; + } + + /** + * <p> + * Append to the <code>toString</code> an <code>Object</code> array. + * </p> + * + * @param array + * the array to add to the <code>toString</code> + * @return this + */ + public ReflectionToStringBuilder reflectionAppendArray(Object array) { + this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array); + return this; + } + + /** + * <p> + * Sets whether or not to append static fields. + * </p> + * + * @param appendStatics + * Whether or not to append static fields. + * @since 2.1 + */ + public void setAppendStatics(boolean appendStatics) { + this.appendStatics = appendStatics; + } + + /** + * <p> + * Sets whether or not to append transient fields. + * </p> + * + * @param appendTransients + * Whether or not to append transient fields. + */ + public void setAppendTransients(boolean appendTransients) { + this.appendTransients = appendTransients; + } + + /** + * Sets the field names to exclude. + * + * @param excludeFieldNamesParam + * The excludeFieldNames to excluding from toString or <code>null</code>. + * @return <code>this</code> + */ + public ReflectionToStringBuilder setExcludeFieldNames(String... excludeFieldNamesParam) { + if (excludeFieldNamesParam == null) { + this.excludeFieldNames = null; + } else { + //clone and remove nulls + this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam); + Arrays.sort(this.excludeFieldNames); + } + return this; + } + + /** + * <p> + * Sets the last super class to stop appending fields for. + * </p> + * + * @param clazz + * The last super class to stop appending fields for. + */ + public void setUpToClass(Class<?> clazz) { + if (clazz != null) { + Object object = getObject(); + if (object != null && clazz.isInstance(object) == false) { + throw new IllegalArgumentException("Specified class is not a superclass of the object"); + } + } + this.upToClass = clazz; + } + + /** + * <p> + * Gets the String built by this builder. + * </p> + * + * @return the built string + */ + @Override + public String toString() { + if (this.getObject() == null) { + return this.getStyle().getNullText(); + } + Class<?> clazz = this.getObject().getClass(); + this.appendFieldsIn(clazz); + while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) { + clazz = clazz.getSuperclass(); + this.appendFieldsIn(clazz); + } + return super.toString(); + } + +} diff --git a/src/org/apache/commons/lang3/builder/StandardToStringStyle.java b/src/org/apache/commons/lang3/builder/StandardToStringStyle.java index b58b154..cec4f09 100644 --- a/src/org/apache/commons/lang3/builder/StandardToStringStyle.java +++ b/src/org/apache/commons/lang3/builder/StandardToStringStyle.java @@ -1,560 +1,560 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-/**
- * <p>Works with {@link ToStringBuilder} to create a <code>toString</code>.</p>
- *
- * <p>This class is intended to be used as a singleton.
- * There is no need to instantiate a new style each time.
- * Simply instantiate the class once, customize the values as required, and
- * store the result in a public static final variable for the rest of the
- * program to access.</p>
- *
- * @since 1.0
- * @version $Id: StandardToStringStyle.java 1089740 2011-04-07 05:01:54Z bayard $
- */
-public class StandardToStringStyle extends ToStringStyle {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- */
- public StandardToStringStyle() {
- super();
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the class name.</p>
- *
- * @return the current useClassName flag
- */
- @Override
- public boolean isUseClassName() { // NOPMD as this is implementing the abstract class
- return super.isUseClassName();
- }
-
- /**
- * <p>Sets whether to use the class name.</p>
- *
- * @param useClassName the new useClassName flag
- */
- @Override
- public void setUseClassName(boolean useClassName) { // NOPMD as this is implementing the abstract class
- super.setUseClassName(useClassName);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to output short or long class names.</p>
- *
- * @return the current useShortClassName flag
- * @since 2.0
- */
- @Override
- public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class
- return super.isUseShortClassName();
- }
-
- /**
- * <p>Sets whether to output short or long class names.</p>
- *
- * @param useShortClassName the new useShortClassName flag
- * @since 2.0
- */
- @Override
- public void setUseShortClassName(boolean useShortClassName) { // NOPMD as this is implementing the abstract class
- super.setUseShortClassName(useShortClassName);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the identity hash code.</p>
- * @return the current useIdentityHashCode flag
- */
- @Override
- public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class
- return super.isUseIdentityHashCode();
- }
-
- /**
- * <p>Sets whether to use the identity hash code.</p>
- *
- * @param useIdentityHashCode the new useIdentityHashCode flag
- */
- @Override
- public void setUseIdentityHashCode(boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class
- super.setUseIdentityHashCode(useIdentityHashCode);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the field names passed in.</p>
- *
- * @return the current useFieldNames flag
- */
- @Override
- public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class
- return super.isUseFieldNames();
- }
-
- /**
- * <p>Sets whether to use the field names passed in.</p>
- *
- * @param useFieldNames the new useFieldNames flag
- */
- @Override
- public void setUseFieldNames(boolean useFieldNames) { // NOPMD as this is implementing the abstract class
- super.setUseFieldNames(useFieldNames);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use full detail when the caller doesn't
- * specify.</p>
- *
- * @return the current defaultFullDetail flag
- */
- @Override
- public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class
- return super.isDefaultFullDetail();
- }
-
- /**
- * <p>Sets whether to use full detail when the caller doesn't
- * specify.</p>
- *
- * @param defaultFullDetail the new defaultFullDetail flag
- */
- @Override
- public void setDefaultFullDetail(boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class
- super.setDefaultFullDetail(defaultFullDetail);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to output array content detail.</p>
- *
- * @return the current array content detail setting
- */
- @Override
- public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class
- return super.isArrayContentDetail();
- }
-
- /**
- * <p>Sets whether to output array content detail.</p>
- *
- * @param arrayContentDetail the new arrayContentDetail flag
- */
- @Override
- public void setArrayContentDetail(boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class
- super.setArrayContentDetail(arrayContentDetail);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array start text.</p>
- *
- * @return the current array start text
- */
- @Override
- public String getArrayStart() { // NOPMD as this is implementing the abstract class
- return super.getArrayStart();
- }
-
- /**
- * <p>Sets the array start text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param arrayStart the new array start text
- */
- @Override
- public void setArrayStart(String arrayStart) { // NOPMD as this is implementing the abstract class
- super.setArrayStart(arrayStart);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array end text.</p>
- *
- * @return the current array end text
- */
- @Override
- public String getArrayEnd() { // NOPMD as this is implementing the abstract class
- return super.getArrayEnd();
- }
-
- /**
- * <p>Sets the array end text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param arrayEnd the new array end text
- */
- @Override
- public void setArrayEnd(String arrayEnd) { // NOPMD as this is implementing the abstract class
- super.setArrayEnd(arrayEnd);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array separator text.</p>
- *
- * @return the current array separator text
- */
- @Override
- public String getArraySeparator() { // NOPMD as this is implementing the abstract class
- return super.getArraySeparator();
- }
-
- /**
- * <p>Sets the array separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param arraySeparator the new array separator text
- */
- @Override
- public void setArraySeparator(String arraySeparator) { // NOPMD as this is implementing the abstract class
- super.setArraySeparator(arraySeparator);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the content start text.</p>
- *
- * @return the current content start text
- */
- @Override
- public String getContentStart() { // NOPMD as this is implementing the abstract class
- return super.getContentStart();
- }
-
- /**
- * <p>Sets the content start text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param contentStart the new content start text
- */
- @Override
- public void setContentStart(String contentStart) { // NOPMD as this is implementing the abstract class
- super.setContentStart(contentStart);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the content end text.</p>
- *
- * @return the current content end text
- */
- @Override
- public String getContentEnd() { // NOPMD as this is implementing the abstract class
- return super.getContentEnd();
- }
-
- /**
- * <p>Sets the content end text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param contentEnd the new content end text
- */
- @Override
- public void setContentEnd(String contentEnd) { // NOPMD as this is implementing the abstract class
- super.setContentEnd(contentEnd);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the field name value separator text.</p>
- *
- * @return the current field name value separator text
- */
- @Override
- public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class
- return super.getFieldNameValueSeparator();
- }
-
- /**
- * <p>Sets the field name value separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param fieldNameValueSeparator the new field name value separator text
- */
- @Override
- public void setFieldNameValueSeparator(String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class
- super.setFieldNameValueSeparator(fieldNameValueSeparator);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the field separator text.</p>
- *
- * @return the current field separator text
- */
- @Override
- public String getFieldSeparator() { // NOPMD as this is implementing the abstract class
- return super.getFieldSeparator();
- }
-
- /**
- * <p>Sets the field separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param fieldSeparator the new field separator text
- */
- @Override
- public void setFieldSeparator(String fieldSeparator) { // NOPMD as this is implementing the abstract class
- super.setFieldSeparator(fieldSeparator);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether the field separator should be added at the start
- * of each buffer.</p>
- *
- * @return the fieldSeparatorAtStart flag
- * @since 2.0
- */
- @Override
- public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class
- return super.isFieldSeparatorAtStart();
- }
-
- /**
- * <p>Sets whether the field separator should be added at the start
- * of each buffer.</p>
- *
- * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
- * @since 2.0
- */
- @Override
- public void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class
- super.setFieldSeparatorAtStart(fieldSeparatorAtStart);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether the field separator should be added at the end
- * of each buffer.</p>
- *
- * @return fieldSeparatorAtEnd flag
- * @since 2.0
- */
- @Override
- public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class
- return super.isFieldSeparatorAtEnd();
- }
-
- /**
- * <p>Sets whether the field separator should be added at the end
- * of each buffer.</p>
- *
- * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
- * @since 2.0
- */
- @Override
- public void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class
- super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the text to output when <code>null</code> found.</p>
- *
- * @return the current text to output when <code>null</code> found
- */
- @Override
- public String getNullText() { // NOPMD as this is implementing the abstract class
- return super.getNullText();
- }
-
- /**
- * <p>Sets the text to output when <code>null</code> found.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param nullText the new text to output when <code>null</code> found
- */
- @Override
- public void setNullText(String nullText) { // NOPMD as this is implementing the abstract class
- super.setNullText(nullText);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the text to output when a <code>Collection</code>,
- * <code>Map</code> or <code>Array</code> size is output.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * @return the current start of size text
- */
- @Override
- public String getSizeStartText() { // NOPMD as this is implementing the abstract class
- return super.getSizeStartText();
- }
-
- /**
- * <p>Sets the start text to output when a <code>Collection</code>,
- * <code>Map</code> or <code>Array</code> size is output.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param sizeStartText the new start of size text
- */
- @Override
- public void setSizeStartText(String sizeStartText) { // NOPMD as this is implementing the abstract class
- super.setSizeStartText(sizeStartText);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * Gets the end text to output when a <code>Collection</code>,
- * <code>Map</code> or <code>Array</code> size is output.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * @return the current end of size text
- */
- @Override
- public String getSizeEndText() { // NOPMD as this is implementing the abstract class
- return super.getSizeEndText();
- }
-
- /**
- * <p>Sets the end text to output when a <code>Collection</code>,
- * <code>Map</code> or <code>Array</code> size is output.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted
- * to an empty String.</p>
- *
- * @param sizeEndText the new end of size text
- */
- @Override
- public void setSizeEndText(String sizeEndText) { // NOPMD as this is implementing the abstract class
- super.setSizeEndText(sizeEndText);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the start text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <P>This is output before the size value.</p>
- *
- * @return the current start of summary text
- */
- @Override
- public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class
- return super.getSummaryObjectStartText();
- }
-
- /**
- * <p>Sets the start text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param summaryObjectStartText the new start of summary text
- */
- @Override
- public void setSummaryObjectStartText(String summaryObjectStartText) { // NOPMD as this is implementing the abstract class
- super.setSummaryObjectStartText(summaryObjectStartText);
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the end text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * @return the current end of summary text
- */
- @Override
- public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class
- return super.getSummaryObjectEndText();
- }
-
- /**
- * <p>Sets the end text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param summaryObjectEndText the new end of summary text
- */
- @Override
- public void setSummaryObjectEndText(String summaryObjectEndText) { // NOPMD as this is implementing the abstract class
- super.setSummaryObjectEndText(summaryObjectEndText);
- }
-
- //---------------------------------------------------------------------
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +/** + * <p>Works with {@link ToStringBuilder} to create a <code>toString</code>.</p> + * + * <p>This class is intended to be used as a singleton. + * There is no need to instantiate a new style each time. + * Simply instantiate the class once, customize the values as required, and + * store the result in a public static final variable for the rest of the + * program to access.</p> + * + * @since 1.0 + * @version $Id: StandardToStringStyle.java 1089740 2011-04-07 05:01:54Z bayard $ + */ +public class StandardToStringStyle extends ToStringStyle { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + */ + public StandardToStringStyle() { + super(); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the class name.</p> + * + * @return the current useClassName flag + */ + @Override + public boolean isUseClassName() { // NOPMD as this is implementing the abstract class + return super.isUseClassName(); + } + + /** + * <p>Sets whether to use the class name.</p> + * + * @param useClassName the new useClassName flag + */ + @Override + public void setUseClassName(boolean useClassName) { // NOPMD as this is implementing the abstract class + super.setUseClassName(useClassName); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to output short or long class names.</p> + * + * @return the current useShortClassName flag + * @since 2.0 + */ + @Override + public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class + return super.isUseShortClassName(); + } + + /** + * <p>Sets whether to output short or long class names.</p> + * + * @param useShortClassName the new useShortClassName flag + * @since 2.0 + */ + @Override + public void setUseShortClassName(boolean useShortClassName) { // NOPMD as this is implementing the abstract class + super.setUseShortClassName(useShortClassName); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the identity hash code.</p> + * @return the current useIdentityHashCode flag + */ + @Override + public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class + return super.isUseIdentityHashCode(); + } + + /** + * <p>Sets whether to use the identity hash code.</p> + * + * @param useIdentityHashCode the new useIdentityHashCode flag + */ + @Override + public void setUseIdentityHashCode(boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class + super.setUseIdentityHashCode(useIdentityHashCode); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the field names passed in.</p> + * + * @return the current useFieldNames flag + */ + @Override + public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class + return super.isUseFieldNames(); + } + + /** + * <p>Sets whether to use the field names passed in.</p> + * + * @param useFieldNames the new useFieldNames flag + */ + @Override + public void setUseFieldNames(boolean useFieldNames) { // NOPMD as this is implementing the abstract class + super.setUseFieldNames(useFieldNames); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use full detail when the caller doesn't + * specify.</p> + * + * @return the current defaultFullDetail flag + */ + @Override + public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class + return super.isDefaultFullDetail(); + } + + /** + * <p>Sets whether to use full detail when the caller doesn't + * specify.</p> + * + * @param defaultFullDetail the new defaultFullDetail flag + */ + @Override + public void setDefaultFullDetail(boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class + super.setDefaultFullDetail(defaultFullDetail); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to output array content detail.</p> + * + * @return the current array content detail setting + */ + @Override + public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class + return super.isArrayContentDetail(); + } + + /** + * <p>Sets whether to output array content detail.</p> + * + * @param arrayContentDetail the new arrayContentDetail flag + */ + @Override + public void setArrayContentDetail(boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class + super.setArrayContentDetail(arrayContentDetail); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array start text.</p> + * + * @return the current array start text + */ + @Override + public String getArrayStart() { // NOPMD as this is implementing the abstract class + return super.getArrayStart(); + } + + /** + * <p>Sets the array start text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param arrayStart the new array start text + */ + @Override + public void setArrayStart(String arrayStart) { // NOPMD as this is implementing the abstract class + super.setArrayStart(arrayStart); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array end text.</p> + * + * @return the current array end text + */ + @Override + public String getArrayEnd() { // NOPMD as this is implementing the abstract class + return super.getArrayEnd(); + } + + /** + * <p>Sets the array end text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param arrayEnd the new array end text + */ + @Override + public void setArrayEnd(String arrayEnd) { // NOPMD as this is implementing the abstract class + super.setArrayEnd(arrayEnd); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array separator text.</p> + * + * @return the current array separator text + */ + @Override + public String getArraySeparator() { // NOPMD as this is implementing the abstract class + return super.getArraySeparator(); + } + + /** + * <p>Sets the array separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param arraySeparator the new array separator text + */ + @Override + public void setArraySeparator(String arraySeparator) { // NOPMD as this is implementing the abstract class + super.setArraySeparator(arraySeparator); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the content start text.</p> + * + * @return the current content start text + */ + @Override + public String getContentStart() { // NOPMD as this is implementing the abstract class + return super.getContentStart(); + } + + /** + * <p>Sets the content start text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param contentStart the new content start text + */ + @Override + public void setContentStart(String contentStart) { // NOPMD as this is implementing the abstract class + super.setContentStart(contentStart); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the content end text.</p> + * + * @return the current content end text + */ + @Override + public String getContentEnd() { // NOPMD as this is implementing the abstract class + return super.getContentEnd(); + } + + /** + * <p>Sets the content end text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param contentEnd the new content end text + */ + @Override + public void setContentEnd(String contentEnd) { // NOPMD as this is implementing the abstract class + super.setContentEnd(contentEnd); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the field name value separator text.</p> + * + * @return the current field name value separator text + */ + @Override + public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class + return super.getFieldNameValueSeparator(); + } + + /** + * <p>Sets the field name value separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param fieldNameValueSeparator the new field name value separator text + */ + @Override + public void setFieldNameValueSeparator(String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class + super.setFieldNameValueSeparator(fieldNameValueSeparator); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the field separator text.</p> + * + * @return the current field separator text + */ + @Override + public String getFieldSeparator() { // NOPMD as this is implementing the abstract class + return super.getFieldSeparator(); + } + + /** + * <p>Sets the field separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param fieldSeparator the new field separator text + */ + @Override + public void setFieldSeparator(String fieldSeparator) { // NOPMD as this is implementing the abstract class + super.setFieldSeparator(fieldSeparator); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether the field separator should be added at the start + * of each buffer.</p> + * + * @return the fieldSeparatorAtStart flag + * @since 2.0 + */ + @Override + public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class + return super.isFieldSeparatorAtStart(); + } + + /** + * <p>Sets whether the field separator should be added at the start + * of each buffer.</p> + * + * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag + * @since 2.0 + */ + @Override + public void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class + super.setFieldSeparatorAtStart(fieldSeparatorAtStart); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether the field separator should be added at the end + * of each buffer.</p> + * + * @return fieldSeparatorAtEnd flag + * @since 2.0 + */ + @Override + public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class + return super.isFieldSeparatorAtEnd(); + } + + /** + * <p>Sets whether the field separator should be added at the end + * of each buffer.</p> + * + * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag + * @since 2.0 + */ + @Override + public void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class + super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the text to output when <code>null</code> found.</p> + * + * @return the current text to output when <code>null</code> found + */ + @Override + public String getNullText() { // NOPMD as this is implementing the abstract class + return super.getNullText(); + } + + /** + * <p>Sets the text to output when <code>null</code> found.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param nullText the new text to output when <code>null</code> found + */ + @Override + public void setNullText(String nullText) { // NOPMD as this is implementing the abstract class + super.setNullText(nullText); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the text to output when a <code>Collection</code>, + * <code>Map</code> or <code>Array</code> size is output.</p> + * + * <p>This is output before the size value.</p> + * + * @return the current start of size text + */ + @Override + public String getSizeStartText() { // NOPMD as this is implementing the abstract class + return super.getSizeStartText(); + } + + /** + * <p>Sets the start text to output when a <code>Collection</code>, + * <code>Map</code> or <code>Array</code> size is output.</p> + * + * <p>This is output before the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param sizeStartText the new start of size text + */ + @Override + public void setSizeStartText(String sizeStartText) { // NOPMD as this is implementing the abstract class + super.setSizeStartText(sizeStartText); + } + + //--------------------------------------------------------------------- + + /** + * Gets the end text to output when a <code>Collection</code>, + * <code>Map</code> or <code>Array</code> size is output.</p> + * + * <p>This is output after the size value.</p> + * + * @return the current end of size text + */ + @Override + public String getSizeEndText() { // NOPMD as this is implementing the abstract class + return super.getSizeEndText(); + } + + /** + * <p>Sets the end text to output when a <code>Collection</code>, + * <code>Map</code> or <code>Array</code> size is output.</p> + * + * <p>This is output after the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted + * to an empty String.</p> + * + * @param sizeEndText the new end of size text + */ + @Override + public void setSizeEndText(String sizeEndText) { // NOPMD as this is implementing the abstract class + super.setSizeEndText(sizeEndText); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the start text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <P>This is output before the size value.</p> + * + * @return the current start of summary text + */ + @Override + public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class + return super.getSummaryObjectStartText(); + } + + /** + * <p>Sets the start text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output before the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param summaryObjectStartText the new start of summary text + */ + @Override + public void setSummaryObjectStartText(String summaryObjectStartText) { // NOPMD as this is implementing the abstract class + super.setSummaryObjectStartText(summaryObjectStartText); + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the end text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output after the size value.</p> + * + * @return the current end of summary text + */ + @Override + public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class + return super.getSummaryObjectEndText(); + } + + /** + * <p>Sets the end text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output after the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param summaryObjectEndText the new end of summary text + */ + @Override + public void setSummaryObjectEndText(String summaryObjectEndText) { // NOPMD as this is implementing the abstract class + super.setSummaryObjectEndText(summaryObjectEndText); + } + + //--------------------------------------------------------------------- + +} diff --git a/src/org/apache/commons/lang3/builder/ToStringBuilder.java b/src/org/apache/commons/lang3/builder/ToStringBuilder.java index 63c6268..1e658aa 100644 --- a/src/org/apache/commons/lang3/builder/ToStringBuilder.java +++ b/src/org/apache/commons/lang3/builder/ToStringBuilder.java @@ -1,1079 +1,1079 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-import org.apache.commons.lang3.ObjectUtils;
-
-/**
- * <p>Assists in implementing {@link Object#toString()} methods.</p>
- *
- * <p>This class enables a good and consistent <code>toString()</code> to be built for any
- * class or object. This class aims to simplify the process by:</p>
- * <ul>
- * <li>allowing field names</li>
- * <li>handling all types consistently</li>
- * <li>handling nulls consistently</li>
- * <li>outputting arrays and multi-dimensional arrays</li>
- * <li>enabling the detail level to be controlled for Objects and Collections</li>
- * <li>handling class hierarchies</li>
- * </ul>
- *
- * <p>To use this class write code as follows:</p>
- *
- * <pre>
- * public class Person {
- * String name;
- * int age;
- * boolean smoker;
- *
- * ...
- *
- * public String toString() {
- * return new ToStringBuilder(this).
- * append("name", name).
- * append("age", age).
- * append("smoker", smoker).
- * toString();
- * }
- * }
- * </pre>
- *
- * <p>This will produce a toString of the format:
- * <code>Person@7f54[name=Stephen,age=29,smoker=false]</code></p>
- *
- * <p>To add the superclass <code>toString</code>, use {@link #appendSuper}.
- * To append the <code>toString</code> from an object that is delegated
- * to (or any other object), use {@link #appendToString}.</p>
- *
- * <p>Alternatively, there is a method that uses reflection to determine
- * the fields to test. Because these fields are usually private, the method,
- * <code>reflectionToString</code>, uses <code>AccessibleObject.setAccessible</code> to
- * change the visibility of the fields. This will fail under a security manager,
- * unless the appropriate permissions are set up correctly. It is also
- * slower than testing explicitly.</p>
- *
- * <p>A typical invocation for this method would look like:</p>
- *
- * <pre>
- * public String toString() {
- * return ToStringBuilder.reflectionToString(this);
- * }
- * </pre>
- *
- * <p>You can also use the builder to debug 3rd party objects:</p>
- *
- * <pre>
- * System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject));
- * </pre>
- *
- * <p>The exact format of the <code>toString</code> is determined by
- * the {@link ToStringStyle} passed into the constructor.</p>
- *
- * @since 1.0
- * @version $Id: ToStringBuilder.java 1088899 2011-04-05 05:31:27Z bayard $
- */
-public class ToStringBuilder implements Builder<String> {
-
- /**
- * The default style of output to use, not null.
- */
- private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE;
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Gets the default <code>ToStringStyle</code> to use.</p>
- *
- * <p>This method gets a singleton default value, typically for the whole JVM.
- * Changing this default should generally only be done during application startup.
- * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead
- * of using this global default.</p>
- *
- * <p>This method can be used from multiple threads.
- * Internally, a <code>volatile</code> variable is used to provide the guarantee
- * that the latest value set using {@link #setDefaultStyle} is the value returned.
- * It is strongly recommended that the default style is only changed during application startup.</p>
- *
- * <p>One reason for changing the default could be to have a verbose style during
- * development and a compact style in production.</p>
- *
- * @return the default <code>ToStringStyle</code>, never null
- */
- public static ToStringStyle getDefaultStyle() {
- return defaultStyle;
- }
-
- /**
- * <p>Sets the default <code>ToStringStyle</code> to use.</p>
- *
- * <p>This method sets a singleton default value, typically for the whole JVM.
- * Changing this default should generally only be done during application startup.
- * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead
- * of changing this global default.</p>
- *
- * <p>This method is not intended for use from multiple threads.
- * Internally, a <code>volatile</code> variable is used to provide the guarantee
- * that the latest value set is the value returned from {@link #getDefaultStyle}.</p>
- *
- * @param style the default <code>ToStringStyle</code>
- * @throws IllegalArgumentException if the style is <code>null</code>
- */
- public static void setDefaultStyle(ToStringStyle style) {
- if (style == null) {
- throw new IllegalArgumentException("The style must not be null");
- }
- defaultStyle = style;
- }
-
- //----------------------------------------------------------------------------
- /**
- * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
- * <code>toString</code> for the specified object.</p>
- *
- * @param object the Object to be output
- * @return the String result
- * @see ReflectionToStringBuilder#toString(Object)
- */
- public static String reflectionToString(Object object) {
- return ReflectionToStringBuilder.toString(object);
- }
-
- /**
- * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
- * <code>toString</code> for the specified object.</p>
- *
- * @param object the Object to be output
- * @param style the style of the <code>toString</code> to create, may be <code>null</code>
- * @return the String result
- * @see ReflectionToStringBuilder#toString(Object,ToStringStyle)
- */
- public static String reflectionToString(Object object, ToStringStyle style) {
- return ReflectionToStringBuilder.toString(object, style);
- }
-
- /**
- * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
- * <code>toString</code> for the specified object.</p>
- *
- * @param object the Object to be output
- * @param style the style of the <code>toString</code> to create, may be <code>null</code>
- * @param outputTransients whether to include transient fields
- * @return the String result
- * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean)
- */
- public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) {
- return ReflectionToStringBuilder.toString(object, style, outputTransients, false, null);
- }
-
- /**
- * <p>Uses <code>ReflectionToStringBuilder</code> to generate a
- * <code>toString</code> for the specified object.</p>
- *
- * @param <T> the type of the object
- * @param object the Object to be output
- * @param style the style of the <code>toString</code> to create, may be <code>null</code>
- * @param outputTransients whether to include transient fields
- * @param reflectUpToClass the superclass to reflect up to (inclusive), may be <code>null</code>
- * @return the String result
- * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,boolean,Class)
- * @since 2.0
- */
- public static <T> String reflectionToString(
- T object,
- ToStringStyle style,
- boolean outputTransients,
- Class<? super T> reflectUpToClass) {
- return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * Current toString buffer, not null.
- */
- private final StringBuffer buffer;
- /**
- * The object being output, may be null.
- */
- private final Object object;
- /**
- * The style of output to use, not null.
- */
- private final ToStringStyle style;
-
- /**
- * <p>Constructs a builder for the specified object using the default output style.</p>
- *
- * <p>This default style is obtained from {@link #getDefaultStyle()}.</p>
- *
- * @param object the Object to build a <code>toString</code> for, not recommended to be null
- */
- public ToStringBuilder(Object object) {
- this(object, null, null);
- }
-
- /**
- * <p>Constructs a builder for the specified object using the a defined output style.</p>
- *
- * <p>If the style is <code>null</code>, the default style is used.</p>
- *
- * @param object the Object to build a <code>toString</code> for, not recommended to be null
- * @param style the style of the <code>toString</code> to create, null uses the default style
- */
- public ToStringBuilder(Object object, ToStringStyle style) {
- this(object, style, null);
- }
-
- /**
- * <p>Constructs a builder for the specified object.</p>
- *
- * <p>If the style is <code>null</code>, the default style is used.</p>
- *
- * <p>If the buffer is <code>null</code>, a new one is created.</p>
- *
- * @param object the Object to build a <code>toString</code> for, not recommended to be null
- * @param style the style of the <code>toString</code> to create, null uses the default style
- * @param buffer the <code>StringBuffer</code> to populate, may be null
- */
- public ToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) {
- if (style == null) {
- style = getDefaultStyle();
- }
- if (buffer == null) {
- buffer = new StringBuffer(512);
- }
- this.buffer = buffer;
- this.style = style;
- this.object = object;
-
- style.appendStart(buffer, object);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(boolean value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(boolean[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(byte value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(byte[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(char value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(char[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(double value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(double[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(float value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(float[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(int value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(int[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(long value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(long[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value.</p>
- *
- * @param obj the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(Object obj) {
- style.append(buffer, null, obj, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(Object[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * value.</p>
- *
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(short value) {
- style.append(buffer, null, value);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * array.</p>
- *
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(short[] array) {
- style.append(buffer, null, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, boolean value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>hashCode</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, boolean[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, boolean[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>byte</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, byte value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code> array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, byte[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, byte[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, char value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, char[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, char[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, double value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, double[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, double[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>float</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, float value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, float[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, float[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, int value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, int[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, int[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, long value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, long[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, long[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param obj the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, Object obj) {
- style.append(buffer, fieldName, obj, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param obj the value to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail,
- * <code>false</code> for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, Object obj, boolean fullDetail) {
- style.append(buffer, fieldName, obj, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, Object[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, Object[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>short</code>
- * value.</p>
- *
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, short value) {
- style.append(buffer, fieldName, value);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * array.</p>
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @return this
- */
- public ToStringBuilder append(String fieldName, short[] array) {
- style.append(buffer, fieldName, array, null);
- return this;
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * array.</p>
- *
- * <p>A boolean parameter controls the level of detail to show.
- * Setting <code>true</code> will output the array in full. Setting
- * <code>false</code> will output a summary, typically the size of
- * the array.
- *
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info
- * @return this
- */
- public ToStringBuilder append(String fieldName, short[] array, boolean fullDetail) {
- style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail));
- return this;
- }
-
- /**
- * <p>Appends with the same format as the default <code>Object toString()
- * </code> method. Appends the class name followed by
- * {@link System#identityHashCode(java.lang.Object)}.</p>
- *
- * @param object the <code>Object</code> whose class name and id to output
- * @return this
- * @since 2.0
- */
- public ToStringBuilder appendAsObjectToString(Object object) {
- ObjectUtils.identityToString(this.getStringBuffer(), object);
- return this;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append the <code>toString</code> from the superclass.</p>
- *
- * <p>This method assumes that the superclass uses the same <code>ToStringStyle</code>
- * as this one.</p>
- *
- * <p>If <code>superToString</code> is <code>null</code>, no change is made.</p>
- *
- * @param superToString the result of <code>super.toString()</code>
- * @return this
- * @since 2.0
- */
- public ToStringBuilder appendSuper(String superToString) {
- if (superToString != null) {
- style.appendSuper(buffer, superToString);
- }
- return this;
- }
-
- /**
- * <p>Append the <code>toString</code> from another object.</p>
- *
- * <p>This method is useful where a class delegates most of the implementation of
- * its properties to another class. You can then call <code>toString()</code> on
- * the other class and pass the result into this method.</p>
- *
- * <pre>
- * private AnotherObject delegate;
- * private String fieldInThisClass;
- *
- * public String toString() {
- * return new ToStringBuilder(this).
- * appendToString(delegate.toString()).
- * append(fieldInThisClass).
- * toString();
- * }</pre>
- *
- * <p>This method assumes that the other object uses the same <code>ToStringStyle</code>
- * as this one.</p>
- *
- * <p>If the <code>toString</code> is <code>null</code>, no change is made.</p>
- *
- * @param toString the result of <code>toString()</code> on another object
- * @return this
- * @since 2.0
- */
- public ToStringBuilder appendToString(String toString) {
- if (toString != null) {
- style.appendToString(buffer, toString);
- }
- return this;
- }
-
- /**
- * <p>Returns the <code>Object</code> being output.</p>
- *
- * @return The object being output.
- * @since 2.0
- */
- public Object getObject() {
- return object;
- }
-
- /**
- * <p>Gets the <code>StringBuffer</code> being populated.</p>
- *
- * @return the <code>StringBuffer</code> being populated
- */
- public StringBuffer getStringBuffer() {
- return buffer;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Gets the <code>ToStringStyle</code> being used.</p>
- *
- * @return the <code>ToStringStyle</code> being used
- * @since 2.0
- */
- public ToStringStyle getStyle() {
- return style;
- }
-
- /**
- * <p>Returns the built <code>toString</code>.</p>
- *
- * <p>This method appends the end of data indicator, and can only be called once.
- * Use {@link #getStringBuffer} to get the current string state.</p>
- *
- * <p>If the object is <code>null</code>, return the style's <code>nullText</code></p>
- *
- * @return the String <code>toString</code>
- */
- @Override
- public String toString() {
- if (this.getObject() == null) {
- this.getStringBuffer().append(this.getStyle().getNullText());
- } else {
- style.appendEnd(this.getStringBuffer(), this.getObject());
- }
- return this.getStringBuffer().toString();
- }
-
- /**
- * Returns the String that was build as an object representation. The
- * default implementation utilizes the {@link #toString()} implementation.
- *
- * @return the String <code>toString</code>
- *
- * @see #toString()
- *
- * @since 3.0
- */
- public String build() {
- return toString();
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import org.apache.commons.lang3.ObjectUtils; + +/** + * <p>Assists in implementing {@link Object#toString()} methods.</p> + * + * <p>This class enables a good and consistent <code>toString()</code> to be built for any + * class or object. This class aims to simplify the process by:</p> + * <ul> + * <li>allowing field names</li> + * <li>handling all types consistently</li> + * <li>handling nulls consistently</li> + * <li>outputting arrays and multi-dimensional arrays</li> + * <li>enabling the detail level to be controlled for Objects and Collections</li> + * <li>handling class hierarchies</li> + * </ul> + * + * <p>To use this class write code as follows:</p> + * + * <pre> + * public class Person { + * String name; + * int age; + * boolean smoker; + * + * ... + * + * public String toString() { + * return new ToStringBuilder(this). + * append("name", name). + * append("age", age). + * append("smoker", smoker). + * toString(); + * } + * } + * </pre> + * + * <p>This will produce a toString of the format: + * <code>Person@7f54[name=Stephen,age=29,smoker=false]</code></p> + * + * <p>To add the superclass <code>toString</code>, use {@link #appendSuper}. + * To append the <code>toString</code> from an object that is delegated + * to (or any other object), use {@link #appendToString}.</p> + * + * <p>Alternatively, there is a method that uses reflection to determine + * the fields to test. Because these fields are usually private, the method, + * <code>reflectionToString</code>, uses <code>AccessibleObject.setAccessible</code> to + * change the visibility of the fields. This will fail under a security manager, + * unless the appropriate permissions are set up correctly. It is also + * slower than testing explicitly.</p> + * + * <p>A typical invocation for this method would look like:</p> + * + * <pre> + * public String toString() { + * return ToStringBuilder.reflectionToString(this); + * } + * </pre> + * + * <p>You can also use the builder to debug 3rd party objects:</p> + * + * <pre> + * System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject)); + * </pre> + * + * <p>The exact format of the <code>toString</code> is determined by + * the {@link ToStringStyle} passed into the constructor.</p> + * + * @since 1.0 + * @version $Id: ToStringBuilder.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class ToStringBuilder implements Builder<String> { + + /** + * The default style of output to use, not null. + */ + private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE; + + //---------------------------------------------------------------------------- + + /** + * <p>Gets the default <code>ToStringStyle</code> to use.</p> + * + * <p>This method gets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead + * of using this global default.</p> + * + * <p>This method can be used from multiple threads. + * Internally, a <code>volatile</code> variable is used to provide the guarantee + * that the latest value set using {@link #setDefaultStyle} is the value returned. + * It is strongly recommended that the default style is only changed during application startup.</p> + * + * <p>One reason for changing the default could be to have a verbose style during + * development and a compact style in production.</p> + * + * @return the default <code>ToStringStyle</code>, never null + */ + public static ToStringStyle getDefaultStyle() { + return defaultStyle; + } + + /** + * <p>Sets the default <code>ToStringStyle</code> to use.</p> + * + * <p>This method sets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a <code>ToStringStyle</code> to the constructor instead + * of changing this global default.</p> + * + * <p>This method is not intended for use from multiple threads. + * Internally, a <code>volatile</code> variable is used to provide the guarantee + * that the latest value set is the value returned from {@link #getDefaultStyle}.</p> + * + * @param style the default <code>ToStringStyle</code> + * @throws IllegalArgumentException if the style is <code>null</code> + */ + public static void setDefaultStyle(ToStringStyle style) { + if (style == null) { + throw new IllegalArgumentException("The style must not be null"); + } + defaultStyle = style; + } + + //---------------------------------------------------------------------------- + /** + * <p>Uses <code>ReflectionToStringBuilder</code> to generate a + * <code>toString</code> for the specified object.</p> + * + * @param object the Object to be output + * @return the String result + * @see ReflectionToStringBuilder#toString(Object) + */ + public static String reflectionToString(Object object) { + return ReflectionToStringBuilder.toString(object); + } + + /** + * <p>Uses <code>ReflectionToStringBuilder</code> to generate a + * <code>toString</code> for the specified object.</p> + * + * @param object the Object to be output + * @param style the style of the <code>toString</code> to create, may be <code>null</code> + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle) + */ + public static String reflectionToString(Object object, ToStringStyle style) { + return ReflectionToStringBuilder.toString(object, style); + } + + /** + * <p>Uses <code>ReflectionToStringBuilder</code> to generate a + * <code>toString</code> for the specified object.</p> + * + * @param object the Object to be output + * @param style the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients whether to include transient fields + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean) + */ + public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) { + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, null); + } + + /** + * <p>Uses <code>ReflectionToStringBuilder</code> to generate a + * <code>toString</code> for the specified object.</p> + * + * @param <T> the type of the object + * @param object the Object to be output + * @param style the style of the <code>toString</code> to create, may be <code>null</code> + * @param outputTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), may be <code>null</code> + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,boolean,Class) + * @since 2.0 + */ + public static <T> String reflectionToString( + T object, + ToStringStyle style, + boolean outputTransients, + Class<? super T> reflectUpToClass) { + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass); + } + + //---------------------------------------------------------------------------- + + /** + * Current toString buffer, not null. + */ + private final StringBuffer buffer; + /** + * The object being output, may be null. + */ + private final Object object; + /** + * The style of output to use, not null. + */ + private final ToStringStyle style; + + /** + * <p>Constructs a builder for the specified object using the default output style.</p> + * + * <p>This default style is obtained from {@link #getDefaultStyle()}.</p> + * + * @param object the Object to build a <code>toString</code> for, not recommended to be null + */ + public ToStringBuilder(Object object) { + this(object, null, null); + } + + /** + * <p>Constructs a builder for the specified object using the a defined output style.</p> + * + * <p>If the style is <code>null</code>, the default style is used.</p> + * + * @param object the Object to build a <code>toString</code> for, not recommended to be null + * @param style the style of the <code>toString</code> to create, null uses the default style + */ + public ToStringBuilder(Object object, ToStringStyle style) { + this(object, style, null); + } + + /** + * <p>Constructs a builder for the specified object.</p> + * + * <p>If the style is <code>null</code>, the default style is used.</p> + * + * <p>If the buffer is <code>null</code>, a new one is created.</p> + * + * @param object the Object to build a <code>toString</code> for, not recommended to be null + * @param style the style of the <code>toString</code> to create, null uses the default style + * @param buffer the <code>StringBuffer</code> to populate, may be null + */ + public ToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { + if (style == null) { + style = getDefaultStyle(); + } + if (buffer == null) { + buffer = new StringBuffer(512); + } + this.buffer = buffer; + this.style = style; + this.object = object; + + style.appendStart(buffer, object); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(boolean value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(boolean[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(byte value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(byte[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(char value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(char[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(double value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(double[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(float value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(float[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(int value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(int[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(long value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(long[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value.</p> + * + * @param obj the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(Object obj) { + style.append(buffer, null, obj, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(Object[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * value.</p> + * + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(short value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * array.</p> + * + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(short[] array) { + style.append(buffer, null, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, boolean value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>hashCode</code> + * @return this + */ + public ToStringBuilder append(String fieldName, boolean[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, boolean[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>byte</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, byte value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, byte[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array. + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, byte[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, char value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, char[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, char[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, double value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, double[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, double[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>float</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, float value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, float[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, float[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, int value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, int[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, int[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, long value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, long[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, long[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value.</p> + * + * @param fieldName the field name + * @param obj the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, Object obj) { + style.append(buffer, fieldName, obj, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value.</p> + * + * @param fieldName the field name + * @param obj the value to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, + * <code>false</code> for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, Object obj, boolean fullDetail) { + style.append(buffer, fieldName, obj, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, Object[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, Object[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Append to the <code>toString</code> an <code>short</code> + * value.</p> + * + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, short value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * array.</p> + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @return this + */ + public ToStringBuilder append(String fieldName, short[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * array.</p> + * + * <p>A boolean parameter controls the level of detail to show. + * Setting <code>true</code> will output the array in full. Setting + * <code>false</code> will output a summary, typically the size of + * the array. + * + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, short[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + * <p>Appends with the same format as the default <code>Object toString() + * </code> method. Appends the class name followed by + * {@link System#identityHashCode(java.lang.Object)}.</p> + * + * @param object the <code>Object</code> whose class name and id to output + * @return this + * @since 2.0 + */ + public ToStringBuilder appendAsObjectToString(Object object) { + ObjectUtils.identityToString(this.getStringBuffer(), object); + return this; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append the <code>toString</code> from the superclass.</p> + * + * <p>This method assumes that the superclass uses the same <code>ToStringStyle</code> + * as this one.</p> + * + * <p>If <code>superToString</code> is <code>null</code>, no change is made.</p> + * + * @param superToString the result of <code>super.toString()</code> + * @return this + * @since 2.0 + */ + public ToStringBuilder appendSuper(String superToString) { + if (superToString != null) { + style.appendSuper(buffer, superToString); + } + return this; + } + + /** + * <p>Append the <code>toString</code> from another object.</p> + * + * <p>This method is useful where a class delegates most of the implementation of + * its properties to another class. You can then call <code>toString()</code> on + * the other class and pass the result into this method.</p> + * + * <pre> + * private AnotherObject delegate; + * private String fieldInThisClass; + * + * public String toString() { + * return new ToStringBuilder(this). + * appendToString(delegate.toString()). + * append(fieldInThisClass). + * toString(); + * }</pre> + * + * <p>This method assumes that the other object uses the same <code>ToStringStyle</code> + * as this one.</p> + * + * <p>If the <code>toString</code> is <code>null</code>, no change is made.</p> + * + * @param toString the result of <code>toString()</code> on another object + * @return this + * @since 2.0 + */ + public ToStringBuilder appendToString(String toString) { + if (toString != null) { + style.appendToString(buffer, toString); + } + return this; + } + + /** + * <p>Returns the <code>Object</code> being output.</p> + * + * @return The object being output. + * @since 2.0 + */ + public Object getObject() { + return object; + } + + /** + * <p>Gets the <code>StringBuffer</code> being populated.</p> + * + * @return the <code>StringBuffer</code> being populated + */ + public StringBuffer getStringBuffer() { + return buffer; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Gets the <code>ToStringStyle</code> being used.</p> + * + * @return the <code>ToStringStyle</code> being used + * @since 2.0 + */ + public ToStringStyle getStyle() { + return style; + } + + /** + * <p>Returns the built <code>toString</code>.</p> + * + * <p>This method appends the end of data indicator, and can only be called once. + * Use {@link #getStringBuffer} to get the current string state.</p> + * + * <p>If the object is <code>null</code>, return the style's <code>nullText</code></p> + * + * @return the String <code>toString</code> + */ + @Override + public String toString() { + if (this.getObject() == null) { + this.getStringBuffer().append(this.getStyle().getNullText()); + } else { + style.appendEnd(this.getStringBuffer(), this.getObject()); + } + return this.getStringBuffer().toString(); + } + + /** + * Returns the String that was build as an object representation. The + * default implementation utilizes the {@link #toString()} implementation. + * + * @return the String <code>toString</code> + * + * @see #toString() + * + * @since 3.0 + */ + public String build() { + return toString(); + } +} diff --git a/src/org/apache/commons/lang3/builder/ToStringStyle.java b/src/org/apache/commons/lang3/builder/ToStringStyle.java index d9ee587..109fefd 100644 --- a/src/org/apache/commons/lang3/builder/ToStringStyle.java +++ b/src/org/apache/commons/lang3/builder/ToStringStyle.java @@ -1,2271 +1,2271 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.builder;
-
-import java.io.Serializable;
-import java.lang.reflect.Array;
-import java.util.Collection;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.SystemUtils;
-
-/**
- * <p>Controls <code>String</code> formatting for {@link ToStringBuilder}.
- * The main public interface is always via <code>ToStringBuilder</code>.</p>
- *
- * <p>These classes are intended to be used as <code>Singletons</code>.
- * There is no need to instantiate a new style each time. A program
- * will generally use one of the predefined constants on this class.
- * Alternatively, the {@link StandardToStringStyle} class can be used
- * to set the individual settings. Thus most styles can be achieved
- * without subclassing.</p>
- *
- * <p>If required, a subclass can override as many or as few of the
- * methods as it requires. Each object type (from <code>boolean</code>
- * to <code>long</code> to <code>Object</code> to <code>int[]</code>) has
- * its own methods to output it. Most have two versions, detail and summary.
- *
- * <p>For example, the detail version of the array based methods will
- * output the whole array, whereas the summary method will just output
- * the array length.</p>
- *
- * <p>If you want to format the output of certain objects, such as dates, you
- * must create a subclass and override a method.
- * <pre>
- * public class MyStyle extends ToStringStyle {
- * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
- * if (value instanceof Date) {
- * value = new SimpleDateFormat("yyyy-MM-dd").format(value);
- * }
- * buffer.append(value);
- * }
- * }
- * </pre>
- * </p>
- *
- * @since 1.0
- * @version $Id: ToStringStyle.java 1091066 2011-04-11 13:30:11Z mbenson $
- */
-public abstract class ToStringStyle implements Serializable {
-
- /**
- * Serialization version ID.
- */
- private static final long serialVersionUID = -2587890625525655916L;
-
- /**
- * The default toString style. Using the Using the <code>Person</code>
- * example from {@link ToStringBuilder}, the output would look like this:
- *
- * <pre>
- * Person@182f0db[name=John Doe,age=33,smoker=false]
- * </pre>
- */
- public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle();
-
- /**
- * The multi line toString style. Using the Using the <code>Person</code>
- * example from {@link ToStringBuilder}, the output would look like this:
- *
- * <pre>
- * Person@182f0db[
- * name=John Doe
- * age=33
- * smoker=false
- * ]
- * </pre>
- */
- public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle();
-
- /**
- * The no field names toString style. Using the Using the
- * <code>Person</code> example from {@link ToStringBuilder}, the output
- * would look like this:
- *
- * <pre>
- * Person@182f0db[John Doe,33,false]
- * </pre>
- */
- public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle();
-
- /**
- * The short prefix toString style. Using the <code>Person</code> example
- * from {@link ToStringBuilder}, the output would look like this:
- *
- * <pre>
- * Person[name=John Doe,age=33,smoker=false]
- * </pre>
- *
- * @since 2.1
- */
- public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle();
-
- /**
- * The simple toString style. Using the Using the <code>Person</code>
- * example from {@link ToStringBuilder}, the output would look like this:
- *
- * <pre>
- * John Doe,33,false
- * </pre>
- */
- public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle();
-
- /**
- * <p>
- * A registry of objects used by <code>reflectionToString</code> methods
- * to detect cyclical object references and avoid infinite loops.
- * </p>
- */
- private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY =
- new ThreadLocal<WeakHashMap<Object,Object>>();
-
- /**
- * <p>
- * Returns the registry of objects being traversed by the <code>reflectionToString</code>
- * methods in the current thread.
- * </p>
- *
- * @return Set the registry of objects being traversed
- */
- static Map<Object, Object> getRegistry() {
- return REGISTRY.get();
- }
-
- /**
- * <p>
- * Returns <code>true</code> if the registry contains the given object.
- * Used by the reflection methods to avoid infinite loops.
- * </p>
- *
- * @param value
- * The object to lookup in the registry.
- * @return boolean <code>true</code> if the registry contains the given
- * object.
- */
- static boolean isRegistered(Object value) {
- Map<Object, Object> m = getRegistry();
- return m != null && m.containsKey(value);
- }
-
- /**
- * <p>
- * Registers the given object. Used by the reflection methods to avoid
- * infinite loops.
- * </p>
- *
- * @param value
- * The object to register.
- */
- static void register(Object value) {
- if (value != null) {
- Map<Object, Object> m = getRegistry();
- if (m == null) {
- REGISTRY.set(new WeakHashMap<Object, Object>());
- }
- getRegistry().put(value, null);
- }
- }
-
- /**
- * <p>
- * Unregisters the given object.
- * </p>
- *
- * <p>
- * Used by the reflection methods to avoid infinite loops.
- * </p>
- *
- * @param value
- * The object to unregister.
- */
- static void unregister(Object value) {
- if (value != null) {
- Map<Object, Object> m = getRegistry();
- if (m != null) {
- m.remove(value);
- if (m.isEmpty()) {
- REGISTRY.remove();
- }
- }
- }
- }
-
- /**
- * Whether to use the field names, the default is <code>true</code>.
- */
- private boolean useFieldNames = true;
-
- /**
- * Whether to use the class name, the default is <code>true</code>.
- */
- private boolean useClassName = true;
-
- /**
- * Whether to use short class names, the default is <code>false</code>.
- */
- private boolean useShortClassName = false;
-
- /**
- * Whether to use the identity hash code, the default is <code>true</code>.
- */
- private boolean useIdentityHashCode = true;
-
- /**
- * The content start <code>'['</code>.
- */
- private String contentStart = "[";
-
- /**
- * The content end <code>']'</code>.
- */
- private String contentEnd = "]";
-
- /**
- * The field name value separator <code>'='</code>.
- */
- private String fieldNameValueSeparator = "=";
-
- /**
- * Whether the field separator should be added before any other fields.
- */
- private boolean fieldSeparatorAtStart = false;
-
- /**
- * Whether the field separator should be added after any other fields.
- */
- private boolean fieldSeparatorAtEnd = false;
-
- /**
- * The field separator <code>','</code>.
- */
- private String fieldSeparator = ",";
-
- /**
- * The array start <code>'{'</code>.
- */
- private String arrayStart = "{";
-
- /**
- * The array separator <code>','</code>.
- */
- private String arraySeparator = ",";
-
- /**
- * The detail for array content.
- */
- private boolean arrayContentDetail = true;
-
- /**
- * The array end <code>'}'</code>.
- */
- private String arrayEnd = "}";
-
- /**
- * The value to use when fullDetail is <code>null</code>,
- * the default value is <code>true</code>.
- */
- private boolean defaultFullDetail = true;
-
- /**
- * The <code>null</code> text <code>'<null>'</code>.
- */
- private String nullText = "<null>";
-
- /**
- * The summary size text start <code>'<size'</code>.
- */
- private String sizeStartText = "<size=";
-
- /**
- * The summary size text start <code>'>'</code>.
- */
- private String sizeEndText = ">";
-
- /**
- * The summary object text start <code>'<'</code>.
- */
- private String summaryObjectStartText = "<";
-
- /**
- * The summary object text start <code>'>'</code>.
- */
- private String summaryObjectEndText = ">";
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Constructor.</p>
- */
- protected ToStringStyle() {
- super();
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> the superclass toString.</p>
- * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
- *
- * <p>A <code>null</code> <code>superToString</code> is ignored.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param superToString the <code>super.toString()</code>
- * @since 2.0
- */
- public void appendSuper(StringBuffer buffer, String superToString) {
- appendToString(buffer, superToString);
- }
-
- /**
- * <p>Append to the <code>toString</code> another toString.</p>
- * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p>
- *
- * <p>A <code>null</code> <code>toString</code> is ignored.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param toString the additional <code>toString</code>
- * @since 2.0
- */
- public void appendToString(StringBuffer buffer, String toString) {
- if (toString != null) {
- int pos1 = toString.indexOf(contentStart) + contentStart.length();
- int pos2 = toString.lastIndexOf(contentEnd);
- if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) {
- String data = toString.substring(pos1, pos2);
- if (fieldSeparatorAtStart) {
- removeLastFieldSeparator(buffer);
- }
- buffer.append(data);
- appendFieldSeparator(buffer);
- }
- }
- }
-
- /**
- * <p>Append to the <code>toString</code> the start of data indicator.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param object the <code>Object</code> to build a <code>toString</code> for
- */
- public void appendStart(StringBuffer buffer, Object object) {
- if (object != null) {
- appendClassName(buffer, object);
- appendIdentityHashCode(buffer, object);
- appendContentStart(buffer);
- if (fieldSeparatorAtStart) {
- appendFieldSeparator(buffer);
- }
- }
- }
-
- /**
- * <p>Append to the <code>toString</code> the end of data indicator.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param object the <code>Object</code> to build a
- * <code>toString</code> for.
- */
- public void appendEnd(StringBuffer buffer, Object object) {
- if (this.fieldSeparatorAtEnd == false) {
- removeLastFieldSeparator(buffer);
- }
- appendContentEnd(buffer);
- unregister(object);
- }
-
- /**
- * <p>Remove the last field separator from the buffer.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @since 2.0
- */
- protected void removeLastFieldSeparator(StringBuffer buffer) {
- int len = buffer.length();
- int sepLen = fieldSeparator.length();
- if (len > 0 && sepLen > 0 && len >= sepLen) {
- boolean match = true;
- for (int i = 0; i < sepLen; i++) {
- if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) {
- match = false;
- break;
- }
- }
- if (match) {
- buffer.setLength(len - sepLen);
- }
- }
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value, printing the full <code>toString</code> of the
- * <code>Object</code> passed in.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (value == null) {
- appendNullText(buffer, fieldName);
-
- } else {
- appendInternal(buffer, fieldName, value, isFullDetail(fullDetail));
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>,
- * correctly interpreting its type.</p>
- *
- * <p>This method performs the main lookup by Class type to correctly
- * route arrays, <code>Collections</code>, <code>Maps</code> and
- * <code>Objects</code> to the appropriate method.</p>
- *
- * <p>Either detail or summary views can be specified.</p>
- *
- * <p>If a cycle is detected, an object will be appended with the
- * <code>Object.toString()</code> format.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>,
- * not <code>null</code>
- * @param detail output detail or not
- */
- protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) {
- if (isRegistered(value)
- && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) {
- appendCyclicObject(buffer, fieldName, value);
- return;
- }
-
- register(value);
-
- try {
- if (value instanceof Collection<?>) {
- if (detail) {
- appendDetail(buffer, fieldName, (Collection<?>) value);
- } else {
- appendSummarySize(buffer, fieldName, ((Collection<?>) value).size());
- }
-
- } else if (value instanceof Map<?, ?>) {
- if (detail) {
- appendDetail(buffer, fieldName, (Map<?, ?>) value);
- } else {
- appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size());
- }
-
- } else if (value instanceof long[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (long[]) value);
- } else {
- appendSummary(buffer, fieldName, (long[]) value);
- }
-
- } else if (value instanceof int[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (int[]) value);
- } else {
- appendSummary(buffer, fieldName, (int[]) value);
- }
-
- } else if (value instanceof short[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (short[]) value);
- } else {
- appendSummary(buffer, fieldName, (short[]) value);
- }
-
- } else if (value instanceof byte[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (byte[]) value);
- } else {
- appendSummary(buffer, fieldName, (byte[]) value);
- }
-
- } else if (value instanceof char[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (char[]) value);
- } else {
- appendSummary(buffer, fieldName, (char[]) value);
- }
-
- } else if (value instanceof double[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (double[]) value);
- } else {
- appendSummary(buffer, fieldName, (double[]) value);
- }
-
- } else if (value instanceof float[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (float[]) value);
- } else {
- appendSummary(buffer, fieldName, (float[]) value);
- }
-
- } else if (value instanceof boolean[]) {
- if (detail) {
- appendDetail(buffer, fieldName, (boolean[]) value);
- } else {
- appendSummary(buffer, fieldName, (boolean[]) value);
- }
-
- } else if (value.getClass().isArray()) {
- if (detail) {
- appendDetail(buffer, fieldName, (Object[]) value);
- } else {
- appendSummary(buffer, fieldName, (Object[]) value);
- }
-
- } else {
- if (detail) {
- appendDetail(buffer, fieldName, value);
- } else {
- appendSummary(buffer, fieldName, value);
- }
- }
- } finally {
- unregister(value);
- }
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value that has been detected to participate in a cycle. This
- * implementation will print the standard string value of the value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>,
- * not <code>null</code>
- *
- * @since 2.2
- */
- protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) {
- ObjectUtils.identityToString(buffer, value);
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value, printing the full detail of the <code>Object</code>.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
- buffer.append(value);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>Collection</code>.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param coll the <code>Collection</code> to add to the
- * <code>toString</code>, not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, Collection<?> coll) {
- buffer.append(coll);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>Map<code>.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param map the <code>Map</code> to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) {
- buffer.append(map);
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * value, printing a summary of the <code>Object</code>.</P>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, Object value) {
- buffer.append(summaryObjectStartText);
- buffer.append(getShortClassName(value.getClass()));
- buffer.append(summaryObjectEndText);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, long value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, long value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, int value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, int value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, short value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, short value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, byte value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, byte value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, char value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, char value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, double value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, double value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, float value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, float value) {
- buffer.append(value);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param value the value to add to the <code>toString</code>
- */
- public void append(StringBuffer buffer, String fieldName, boolean value) {
- appendFieldStart(buffer, fieldName);
- appendDetail(buffer, fieldName, value);
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * value.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param value the value to add to the <code>toString</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) {
- buffer.append(value);
- }
-
- /**
- * <p>Append to the <code>toString</code> an <code>Object</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the toString
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> the detail of an
- * <code>Object</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- Object item = array[i];
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- if (item == null) {
- appendNullText(buffer, fieldName);
-
- } else {
- appendInternal(buffer, fieldName, item, arrayContentDetail);
- }
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of an array type.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- * @since 2.0
- */
- protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) {
- buffer.append(arrayStart);
- int length = Array.getLength(array);
- for (int i = 0; i < length; i++) {
- Object item = Array.get(array, i);
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- if (item == null) {
- appendNullText(buffer, fieldName);
-
- } else {
- appendInternal(buffer, fieldName, item, arrayContentDetail);
- }
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of an
- * <code>Object</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>long</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>long</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>long</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> an <code>int</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of an
- * <code>int</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of an
- * <code>int</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>short</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>short</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>short</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>byte</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>byte</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>byte</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>char</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the <code>toString</code>
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>char</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>char</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>double</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the toString
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>double</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, double[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>double</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, double[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>float</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the toString
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, float[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>float</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, float[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>float</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, float[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> a <code>boolean</code>
- * array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- * @param array the array to add to the toString
- * @param fullDetail <code>true</code> for detail, <code>false</code>
- * for summary info, <code>null</code> for style decides
- */
- public void append(StringBuffer buffer, String fieldName, boolean[] array, Boolean fullDetail) {
- appendFieldStart(buffer, fieldName);
-
- if (array == null) {
- appendNullText(buffer, fieldName);
-
- } else if (isFullDetail(fullDetail)) {
- appendDetail(buffer, fieldName, array);
-
- } else {
- appendSummary(buffer, fieldName, array);
- }
-
- appendFieldEnd(buffer, fieldName);
- }
-
- /**
- * <p>Append to the <code>toString</code> the detail of a
- * <code>boolean</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendDetail(StringBuffer buffer, String fieldName, boolean[] array) {
- buffer.append(arrayStart);
- for (int i = 0; i < array.length; i++) {
- if (i > 0) {
- buffer.append(arraySeparator);
- }
- appendDetail(buffer, fieldName, array[i]);
- }
- buffer.append(arrayEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> a summary of a
- * <code>boolean</code> array.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param array the array to add to the <code>toString</code>,
- * not <code>null</code>
- */
- protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] array) {
- appendSummarySize(buffer, fieldName, array.length);
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Append to the <code>toString</code> the class name.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param object the <code>Object</code> whose name to output
- */
- protected void appendClassName(StringBuffer buffer, Object object) {
- if (useClassName && object != null) {
- register(object);
- if (useShortClassName) {
- buffer.append(getShortClassName(object.getClass()));
- } else {
- buffer.append(object.getClass().getName());
- }
- }
- }
-
- /**
- * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param object the <code>Object</code> whose id to output
- */
- protected void appendIdentityHashCode(StringBuffer buffer, Object object) {
- if (this.isUseIdentityHashCode() && object!=null) {
- register(object);
- buffer.append('@');
- buffer.append(Integer.toHexString(System.identityHashCode(object)));
- }
- }
-
- /**
- * <p>Append to the <code>toString</code> the content start.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- */
- protected void appendContentStart(StringBuffer buffer) {
- buffer.append(contentStart);
- }
-
- /**
- * <p>Append to the <code>toString</code> the content end.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- */
- protected void appendContentEnd(StringBuffer buffer) {
- buffer.append(contentEnd);
- }
-
- /**
- * <p>Append to the <code>toString</code> an indicator for <code>null</code>.</p>
- *
- * <p>The default indicator is <code>'<null>'</code>.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- */
- protected void appendNullText(StringBuffer buffer, String fieldName) {
- buffer.append(nullText);
- }
-
- /**
- * <p>Append to the <code>toString</code> the field separator.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- */
- protected void appendFieldSeparator(StringBuffer buffer) {
- buffer.append(fieldSeparator);
- }
-
- /**
- * <p>Append to the <code>toString</code> the field start.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name
- */
- protected void appendFieldStart(StringBuffer buffer, String fieldName) {
- if (useFieldNames && fieldName != null) {
- buffer.append(fieldName);
- buffer.append(fieldNameValueSeparator);
- }
- }
-
- /**
- * <p>Append to the <code>toString<code> the field end.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- */
- protected void appendFieldEnd(StringBuffer buffer, String fieldName) {
- appendFieldSeparator(buffer);
- }
-
- /**
- * <p>Append to the <code>toString</code> a size summary.</p>
- *
- * <p>The size summary is used to summarize the contents of
- * <code>Collections</code>, <code>Maps</code> and arrays.</p>
- *
- * <p>The output consists of a prefix, the passed in size
- * and a suffix.</p>
- *
- * <p>The default format is <code>'<size=n>'<code>.</p>
- *
- * @param buffer the <code>StringBuffer</code> to populate
- * @param fieldName the field name, typically not used as already appended
- * @param size the size to append
- */
- protected void appendSummarySize(StringBuffer buffer, String fieldName, int size) {
- buffer.append(sizeStartText);
- buffer.append(size);
- buffer.append(sizeEndText);
- }
-
- /**
- * <p>Is this field to be output in full detail.</p>
- *
- * <p>This method converts a detail request into a detail level.
- * The calling code may request full detail (<code>true</code>),
- * but a subclass might ignore that and always return
- * <code>false</code>. The calling code may pass in
- * <code>null</code> indicating that it doesn't care about
- * the detail level. In this case the default detail level is
- * used.</p>
- *
- * @param fullDetailRequest the detail level requested
- * @return whether full detail is to be shown
- */
- protected boolean isFullDetail(Boolean fullDetailRequest) {
- if (fullDetailRequest == null) {
- return defaultFullDetail;
- }
- return fullDetailRequest.booleanValue();
- }
-
- /**
- * <p>Gets the short class name for a class.</p>
- *
- * <p>The short class name is the classname excluding
- * the package name.</p>
- *
- * @param cls the <code>Class</code> to get the short name of
- * @return the short name
- */
- protected String getShortClassName(Class<?> cls) {
- return ClassUtils.getShortClassName(cls);
- }
-
- // Setters and getters for the customizable parts of the style
- // These methods are not expected to be overridden, except to make public
- // (They are not public so that immutable subclasses can be written)
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the class name.</p>
- *
- * @return the current useClassName flag
- */
- protected boolean isUseClassName() {
- return useClassName;
- }
-
- /**
- * <p>Sets whether to use the class name.</p>
- *
- * @param useClassName the new useClassName flag
- */
- protected void setUseClassName(boolean useClassName) {
- this.useClassName = useClassName;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to output short or long class names.</p>
- *
- * @return the current useShortClassName flag
- * @since 2.0
- */
- protected boolean isUseShortClassName() {
- return useShortClassName;
- }
-
- /**
- * <p>Sets whether to output short or long class names.</p>
- *
- * @param useShortClassName the new useShortClassName flag
- * @since 2.0
- */
- protected void setUseShortClassName(boolean useShortClassName) {
- this.useShortClassName = useShortClassName;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the identity hash code.</p>
- *
- * @return the current useIdentityHashCode flag
- */
- protected boolean isUseIdentityHashCode() {
- return useIdentityHashCode;
- }
-
- /**
- * <p>Sets whether to use the identity hash code.</p>
- *
- * @param useIdentityHashCode the new useIdentityHashCode flag
- */
- protected void setUseIdentityHashCode(boolean useIdentityHashCode) {
- this.useIdentityHashCode = useIdentityHashCode;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use the field names passed in.</p>
- *
- * @return the current useFieldNames flag
- */
- protected boolean isUseFieldNames() {
- return useFieldNames;
- }
-
- /**
- * <p>Sets whether to use the field names passed in.</p>
- *
- * @param useFieldNames the new useFieldNames flag
- */
- protected void setUseFieldNames(boolean useFieldNames) {
- this.useFieldNames = useFieldNames;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to use full detail when the caller doesn't
- * specify.</p>
- *
- * @return the current defaultFullDetail flag
- */
- protected boolean isDefaultFullDetail() {
- return defaultFullDetail;
- }
-
- /**
- * <p>Sets whether to use full detail when the caller doesn't
- * specify.</p>
- *
- * @param defaultFullDetail the new defaultFullDetail flag
- */
- protected void setDefaultFullDetail(boolean defaultFullDetail) {
- this.defaultFullDetail = defaultFullDetail;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether to output array content detail.</p>
- *
- * @return the current array content detail setting
- */
- protected boolean isArrayContentDetail() {
- return arrayContentDetail;
- }
-
- /**
- * <p>Sets whether to output array content detail.</p>
- *
- * @param arrayContentDetail the new arrayContentDetail flag
- */
- protected void setArrayContentDetail(boolean arrayContentDetail) {
- this.arrayContentDetail = arrayContentDetail;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array start text.</p>
- *
- * @return the current array start text
- */
- protected String getArrayStart() {
- return arrayStart;
- }
-
- /**
- * <p>Sets the array start text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param arrayStart the new array start text
- */
- protected void setArrayStart(String arrayStart) {
- if (arrayStart == null) {
- arrayStart = "";
- }
- this.arrayStart = arrayStart;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array end text.</p>
- *
- * @return the current array end text
- */
- protected String getArrayEnd() {
- return arrayEnd;
- }
-
- /**
- * <p>Sets the array end text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param arrayEnd the new array end text
- */
- protected void setArrayEnd(String arrayEnd) {
- if (arrayEnd == null) {
- arrayEnd = "";
- }
- this.arrayEnd = arrayEnd;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the array separator text.</p>
- *
- * @return the current array separator text
- */
- protected String getArraySeparator() {
- return arraySeparator;
- }
-
- /**
- * <p>Sets the array separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param arraySeparator the new array separator text
- */
- protected void setArraySeparator(String arraySeparator) {
- if (arraySeparator == null) {
- arraySeparator = "";
- }
- this.arraySeparator = arraySeparator;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the content start text.</p>
- *
- * @return the current content start text
- */
- protected String getContentStart() {
- return contentStart;
- }
-
- /**
- * <p>Sets the content start text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param contentStart the new content start text
- */
- protected void setContentStart(String contentStart) {
- if (contentStart == null) {
- contentStart = "";
- }
- this.contentStart = contentStart;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the content end text.</p>
- *
- * @return the current content end text
- */
- protected String getContentEnd() {
- return contentEnd;
- }
-
- /**
- * <p>Sets the content end text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param contentEnd the new content end text
- */
- protected void setContentEnd(String contentEnd) {
- if (contentEnd == null) {
- contentEnd = "";
- }
- this.contentEnd = contentEnd;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the field name value separator text.</p>
- *
- * @return the current field name value separator text
- */
- protected String getFieldNameValueSeparator() {
- return fieldNameValueSeparator;
- }
-
- /**
- * <p>Sets the field name value separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param fieldNameValueSeparator the new field name value separator text
- */
- protected void setFieldNameValueSeparator(String fieldNameValueSeparator) {
- if (fieldNameValueSeparator == null) {
- fieldNameValueSeparator = "";
- }
- this.fieldNameValueSeparator = fieldNameValueSeparator;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the field separator text.</p>
- *
- * @return the current field separator text
- */
- protected String getFieldSeparator() {
- return fieldSeparator;
- }
-
- /**
- * <p>Sets the field separator text.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param fieldSeparator the new field separator text
- */
- protected void setFieldSeparator(String fieldSeparator) {
- if (fieldSeparator == null) {
- fieldSeparator = "";
- }
- this.fieldSeparator = fieldSeparator;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether the field separator should be added at the start
- * of each buffer.</p>
- *
- * @return the fieldSeparatorAtStart flag
- * @since 2.0
- */
- protected boolean isFieldSeparatorAtStart() {
- return fieldSeparatorAtStart;
- }
-
- /**
- * <p>Sets whether the field separator should be added at the start
- * of each buffer.</p>
- *
- * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag
- * @since 2.0
- */
- protected void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) {
- this.fieldSeparatorAtStart = fieldSeparatorAtStart;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets whether the field separator should be added at the end
- * of each buffer.</p>
- *
- * @return fieldSeparatorAtEnd flag
- * @since 2.0
- */
- protected boolean isFieldSeparatorAtEnd() {
- return fieldSeparatorAtEnd;
- }
-
- /**
- * <p>Sets whether the field separator should be added at the end
- * of each buffer.</p>
- *
- * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag
- * @since 2.0
- */
- protected void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) {
- this.fieldSeparatorAtEnd = fieldSeparatorAtEnd;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the text to output when <code>null</code> found.</p>
- *
- * @return the current text to output when null found
- */
- protected String getNullText() {
- return nullText;
- }
-
- /**
- * <p>Sets the text to output when <code>null</code> found.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param nullText the new text to output when null found
- */
- protected void setNullText(String nullText) {
- if (nullText == null) {
- nullText = "";
- }
- this.nullText = nullText;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the start text to output when a <code>Collection</code>,
- * <code>Map</code> or array size is output.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * @return the current start of size text
- */
- protected String getSizeStartText() {
- return sizeStartText;
- }
-
- /**
- * <p>Sets the start text to output when a <code>Collection</code>,
- * <code>Map</code> or array size is output.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param sizeStartText the new start of size text
- */
- protected void setSizeStartText(String sizeStartText) {
- if (sizeStartText == null) {
- sizeStartText = "";
- }
- this.sizeStartText = sizeStartText;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the end text to output when a <code>Collection</code>,
- * <code>Map</code> or array size is output.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * @return the current end of size text
- */
- protected String getSizeEndText() {
- return sizeEndText;
- }
-
- /**
- * <p>Sets the end text to output when a <code>Collection</code>,
- * <code>Map</code> or array size is output.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param sizeEndText the new end of size text
- */
- protected void setSizeEndText(String sizeEndText) {
- if (sizeEndText == null) {
- sizeEndText = "";
- }
- this.sizeEndText = sizeEndText;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the start text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * @return the current start of summary text
- */
- protected String getSummaryObjectStartText() {
- return summaryObjectStartText;
- }
-
- /**
- * <p>Sets the start text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output before the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param summaryObjectStartText the new start of summary text
- */
- protected void setSummaryObjectStartText(String summaryObjectStartText) {
- if (summaryObjectStartText == null) {
- summaryObjectStartText = "";
- }
- this.summaryObjectStartText = summaryObjectStartText;
- }
-
- //---------------------------------------------------------------------
-
- /**
- * <p>Gets the end text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * @return the current end of summary text
- */
- protected String getSummaryObjectEndText() {
- return summaryObjectEndText;
- }
-
- /**
- * <p>Sets the end text to output when an <code>Object</code> is
- * output in summary mode.</p>
- *
- * <p>This is output after the size value.</p>
- *
- * <p><code>null</code> is accepted, but will be converted to
- * an empty String.</p>
- *
- * @param summaryObjectEndText the new end of summary text
- */
- protected void setSummaryObjectEndText(String summaryObjectEndText) {
- if (summaryObjectEndText == null) {
- summaryObjectEndText = "";
- }
- this.summaryObjectEndText = summaryObjectEndText;
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p>Default <code>ToStringStyle</code>.</p>
- *
- * <p>This is an inner class rather than using
- * <code>StandardToStringStyle</code> to ensure its immutability.</p>
- */
- private static final class DefaultToStringStyle extends ToStringStyle {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- *
- * <p>Use the static constant rather than instantiating.</p>
- */
- DefaultToStringStyle() {
- super();
- }
-
- /**
- * <p>Ensure <code>Singleton</code> after serialization.</p>
- *
- * @return the singleton
- */
- private Object readResolve() {
- return ToStringStyle.DEFAULT_STYLE;
- }
-
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p><code>ToStringStyle</code> that does not print out
- * the field names.</p>
- *
- * <p>This is an inner class rather than using
- * <code>StandardToStringStyle</code> to ensure its immutability.
- */
- private static final class NoFieldNameToStringStyle extends ToStringStyle {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- *
- * <p>Use the static constant rather than instantiating.</p>
- */
- NoFieldNameToStringStyle() {
- super();
- this.setUseFieldNames(false);
- }
-
- /**
- * <p>Ensure <code>Singleton</code> after serialization.</p>
- *
- * @return the singleton
- */
- private Object readResolve() {
- return ToStringStyle.NO_FIELD_NAMES_STYLE;
- }
-
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p><code>ToStringStyle</code> that prints out the short
- * class name and no identity hashcode.</p>
- *
- * <p>This is an inner class rather than using
- * <code>StandardToStringStyle</code> to ensure its immutability.</p>
- */
- private static final class ShortPrefixToStringStyle extends ToStringStyle {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- *
- * <p>Use the static constant rather than instantiating.</p>
- */
- ShortPrefixToStringStyle() {
- super();
- this.setUseShortClassName(true);
- this.setUseIdentityHashCode(false);
- }
-
- /**
- * <p>Ensure <code>Singleton</ode> after serialization.</p>
- * @return the singleton
- */
- private Object readResolve() {
- return ToStringStyle.SHORT_PREFIX_STYLE;
- }
-
- }
-
- /**
- * <p><code>ToStringStyle</code> that does not print out the
- * classname, identity hashcode, content start or field name.</p>
- *
- * <p>This is an inner class rather than using
- * <code>StandardToStringStyle</code> to ensure its immutability.</p>
- */
- private static final class SimpleToStringStyle extends ToStringStyle {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- *
- * <p>Use the static constant rather than instantiating.</p>
- */
- SimpleToStringStyle() {
- super();
- this.setUseClassName(false);
- this.setUseIdentityHashCode(false);
- this.setUseFieldNames(false);
- this.setContentStart("");
- this.setContentEnd("");
- }
-
- /**
- * <p>Ensure <code>Singleton</ode> after serialization.</p>
- * @return the singleton
- */
- private Object readResolve() {
- return ToStringStyle.SIMPLE_STYLE;
- }
-
- }
-
- //----------------------------------------------------------------------------
-
- /**
- * <p><code>ToStringStyle</code> that outputs on multiple lines.</p>
- *
- * <p>This is an inner class rather than using
- * <code>StandardToStringStyle</code> to ensure its immutability.</p>
- */
- private static final class MultiLineToStringStyle extends ToStringStyle {
-
- private static final long serialVersionUID = 1L;
-
- /**
- * <p>Constructor.</p>
- *
- * <p>Use the static constant rather than instantiating.</p>
- */
- MultiLineToStringStyle() {
- super();
- this.setContentStart("[");
- this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " ");
- this.setFieldSeparatorAtStart(true);
- this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]");
- }
-
- /**
- * <p>Ensure <code>Singleton</code> after serialization.</p>
- *
- * @return the singleton
- */
- private Object readResolve() {
- return ToStringStyle.MULTI_LINE_STYLE;
- }
-
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + * <p>Controls <code>String</code> formatting for {@link ToStringBuilder}. + * The main public interface is always via <code>ToStringBuilder</code>.</p> + * + * <p>These classes are intended to be used as <code>Singletons</code>. + * There is no need to instantiate a new style each time. A program + * will generally use one of the predefined constants on this class. + * Alternatively, the {@link StandardToStringStyle} class can be used + * to set the individual settings. Thus most styles can be achieved + * without subclassing.</p> + * + * <p>If required, a subclass can override as many or as few of the + * methods as it requires. Each object type (from <code>boolean</code> + * to <code>long</code> to <code>Object</code> to <code>int[]</code>) has + * its own methods to output it. Most have two versions, detail and summary. + * + * <p>For example, the detail version of the array based methods will + * output the whole array, whereas the summary method will just output + * the array length.</p> + * + * <p>If you want to format the output of certain objects, such as dates, you + * must create a subclass and override a method. + * <pre> + * public class MyStyle extends ToStringStyle { + * protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { + * if (value instanceof Date) { + * value = new SimpleDateFormat("yyyy-MM-dd").format(value); + * } + * buffer.append(value); + * } + * } + * </pre> + * </p> + * + * @since 1.0 + * @version $Id: ToStringStyle.java 1091066 2011-04-11 13:30:11Z mbenson $ + */ +public abstract class ToStringStyle implements Serializable { + + /** + * Serialization version ID. + */ + private static final long serialVersionUID = -2587890625525655916L; + + /** + * The default toString style. Using the Using the <code>Person</code> + * example from {@link ToStringBuilder}, the output would look like this: + * + * <pre> + * Person@182f0db[name=John Doe,age=33,smoker=false] + * </pre> + */ + public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); + + /** + * The multi line toString style. Using the Using the <code>Person</code> + * example from {@link ToStringBuilder}, the output would look like this: + * + * <pre> + * Person@182f0db[ + * name=John Doe + * age=33 + * smoker=false + * ] + * </pre> + */ + public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); + + /** + * The no field names toString style. Using the Using the + * <code>Person</code> example from {@link ToStringBuilder}, the output + * would look like this: + * + * <pre> + * Person@182f0db[John Doe,33,false] + * </pre> + */ + public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); + + /** + * The short prefix toString style. Using the <code>Person</code> example + * from {@link ToStringBuilder}, the output would look like this: + * + * <pre> + * Person[name=John Doe,age=33,smoker=false] + * </pre> + * + * @since 2.1 + */ + public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle(); + + /** + * The simple toString style. Using the Using the <code>Person</code> + * example from {@link ToStringBuilder}, the output would look like this: + * + * <pre> + * John Doe,33,false + * </pre> + */ + public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); + + /** + * <p> + * A registry of objects used by <code>reflectionToString</code> methods + * to detect cyclical object references and avoid infinite loops. + * </p> + */ + private static final ThreadLocal<WeakHashMap<Object, Object>> REGISTRY = + new ThreadLocal<WeakHashMap<Object,Object>>(); + + /** + * <p> + * Returns the registry of objects being traversed by the <code>reflectionToString</code> + * methods in the current thread. + * </p> + * + * @return Set the registry of objects being traversed + */ + static Map<Object, Object> getRegistry() { + return REGISTRY.get(); + } + + /** + * <p> + * Returns <code>true</code> if the registry contains the given object. + * Used by the reflection methods to avoid infinite loops. + * </p> + * + * @param value + * The object to lookup in the registry. + * @return boolean <code>true</code> if the registry contains the given + * object. + */ + static boolean isRegistered(Object value) { + Map<Object, Object> m = getRegistry(); + return m != null && m.containsKey(value); + } + + /** + * <p> + * Registers the given object. Used by the reflection methods to avoid + * infinite loops. + * </p> + * + * @param value + * The object to register. + */ + static void register(Object value) { + if (value != null) { + Map<Object, Object> m = getRegistry(); + if (m == null) { + REGISTRY.set(new WeakHashMap<Object, Object>()); + } + getRegistry().put(value, null); + } + } + + /** + * <p> + * Unregisters the given object. + * </p> + * + * <p> + * Used by the reflection methods to avoid infinite loops. + * </p> + * + * @param value + * The object to unregister. + */ + static void unregister(Object value) { + if (value != null) { + Map<Object, Object> m = getRegistry(); + if (m != null) { + m.remove(value); + if (m.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * Whether to use the field names, the default is <code>true</code>. + */ + private boolean useFieldNames = true; + + /** + * Whether to use the class name, the default is <code>true</code>. + */ + private boolean useClassName = true; + + /** + * Whether to use short class names, the default is <code>false</code>. + */ + private boolean useShortClassName = false; + + /** + * Whether to use the identity hash code, the default is <code>true</code>. + */ + private boolean useIdentityHashCode = true; + + /** + * The content start <code>'['</code>. + */ + private String contentStart = "["; + + /** + * The content end <code>']'</code>. + */ + private String contentEnd = "]"; + + /** + * The field name value separator <code>'='</code>. + */ + private String fieldNameValueSeparator = "="; + + /** + * Whether the field separator should be added before any other fields. + */ + private boolean fieldSeparatorAtStart = false; + + /** + * Whether the field separator should be added after any other fields. + */ + private boolean fieldSeparatorAtEnd = false; + + /** + * The field separator <code>','</code>. + */ + private String fieldSeparator = ","; + + /** + * The array start <code>'{'</code>. + */ + private String arrayStart = "{"; + + /** + * The array separator <code>','</code>. + */ + private String arraySeparator = ","; + + /** + * The detail for array content. + */ + private boolean arrayContentDetail = true; + + /** + * The array end <code>'}'</code>. + */ + private String arrayEnd = "}"; + + /** + * The value to use when fullDetail is <code>null</code>, + * the default value is <code>true</code>. + */ + private boolean defaultFullDetail = true; + + /** + * The <code>null</code> text <code>'<null>'</code>. + */ + private String nullText = "<null>"; + + /** + * The summary size text start <code>'<size'</code>. + */ + private String sizeStartText = "<size="; + + /** + * The summary size text start <code>'>'</code>. + */ + private String sizeEndText = ">"; + + /** + * The summary object text start <code>'<'</code>. + */ + private String summaryObjectStartText = "<"; + + /** + * The summary object text start <code>'>'</code>. + */ + private String summaryObjectEndText = ">"; + + //---------------------------------------------------------------------------- + + /** + * <p>Constructor.</p> + */ + protected ToStringStyle() { + super(); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> the superclass toString.</p> + * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p> + * + * <p>A <code>null</code> <code>superToString</code> is ignored.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param superToString the <code>super.toString()</code> + * @since 2.0 + */ + public void appendSuper(StringBuffer buffer, String superToString) { + appendToString(buffer, superToString); + } + + /** + * <p>Append to the <code>toString</code> another toString.</p> + * <p>NOTE: It assumes that the toString has been created from the same ToStringStyle. </p> + * + * <p>A <code>null</code> <code>toString</code> is ignored.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param toString the additional <code>toString</code> + * @since 2.0 + */ + public void appendToString(StringBuffer buffer, String toString) { + if (toString != null) { + int pos1 = toString.indexOf(contentStart) + contentStart.length(); + int pos2 = toString.lastIndexOf(contentEnd); + if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) { + String data = toString.substring(pos1, pos2); + if (fieldSeparatorAtStart) { + removeLastFieldSeparator(buffer); + } + buffer.append(data); + appendFieldSeparator(buffer); + } + } + } + + /** + * <p>Append to the <code>toString</code> the start of data indicator.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param object the <code>Object</code> to build a <code>toString</code> for + */ + public void appendStart(StringBuffer buffer, Object object) { + if (object != null) { + appendClassName(buffer, object); + appendIdentityHashCode(buffer, object); + appendContentStart(buffer); + if (fieldSeparatorAtStart) { + appendFieldSeparator(buffer); + } + } + } + + /** + * <p>Append to the <code>toString</code> the end of data indicator.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param object the <code>Object</code> to build a + * <code>toString</code> for. + */ + public void appendEnd(StringBuffer buffer, Object object) { + if (this.fieldSeparatorAtEnd == false) { + removeLastFieldSeparator(buffer); + } + appendContentEnd(buffer); + unregister(object); + } + + /** + * <p>Remove the last field separator from the buffer.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @since 2.0 + */ + protected void removeLastFieldSeparator(StringBuffer buffer) { + int len = buffer.length(); + int sepLen = fieldSeparator.length(); + if (len > 0 && sepLen > 0 && len >= sepLen) { + boolean match = true; + for (int i = 0; i < sepLen; i++) { + if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) { + match = false; + break; + } + } + if (match) { + buffer.setLength(len - sepLen); + } + } + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value, printing the full <code>toString</code> of the + * <code>Object</code> passed in.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (value == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, value, isFullDetail(fullDetail)); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code>, + * correctly interpreting its type.</p> + * + * <p>This method performs the main lookup by Class type to correctly + * route arrays, <code>Collections</code>, <code>Maps</code> and + * <code>Objects</code> to the appropriate method.</p> + * + * <p>Either detail or summary views can be specified.</p> + * + * <p>If a cycle is detected, an object will be appended with the + * <code>Object.toString()</code> format.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code>, + * not <code>null</code> + * @param detail output detail or not + */ + protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) { + if (isRegistered(value) + && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { + appendCyclicObject(buffer, fieldName, value); + return; + } + + register(value); + + try { + if (value instanceof Collection<?>) { + if (detail) { + appendDetail(buffer, fieldName, (Collection<?>) value); + } else { + appendSummarySize(buffer, fieldName, ((Collection<?>) value).size()); + } + + } else if (value instanceof Map<?, ?>) { + if (detail) { + appendDetail(buffer, fieldName, (Map<?, ?>) value); + } else { + appendSummarySize(buffer, fieldName, ((Map<?, ?>) value).size()); + } + + } else if (value instanceof long[]) { + if (detail) { + appendDetail(buffer, fieldName, (long[]) value); + } else { + appendSummary(buffer, fieldName, (long[]) value); + } + + } else if (value instanceof int[]) { + if (detail) { + appendDetail(buffer, fieldName, (int[]) value); + } else { + appendSummary(buffer, fieldName, (int[]) value); + } + + } else if (value instanceof short[]) { + if (detail) { + appendDetail(buffer, fieldName, (short[]) value); + } else { + appendSummary(buffer, fieldName, (short[]) value); + } + + } else if (value instanceof byte[]) { + if (detail) { + appendDetail(buffer, fieldName, (byte[]) value); + } else { + appendSummary(buffer, fieldName, (byte[]) value); + } + + } else if (value instanceof char[]) { + if (detail) { + appendDetail(buffer, fieldName, (char[]) value); + } else { + appendSummary(buffer, fieldName, (char[]) value); + } + + } else if (value instanceof double[]) { + if (detail) { + appendDetail(buffer, fieldName, (double[]) value); + } else { + appendSummary(buffer, fieldName, (double[]) value); + } + + } else if (value instanceof float[]) { + if (detail) { + appendDetail(buffer, fieldName, (float[]) value); + } else { + appendSummary(buffer, fieldName, (float[]) value); + } + + } else if (value instanceof boolean[]) { + if (detail) { + appendDetail(buffer, fieldName, (boolean[]) value); + } else { + appendSummary(buffer, fieldName, (boolean[]) value); + } + + } else if (value.getClass().isArray()) { + if (detail) { + appendDetail(buffer, fieldName, (Object[]) value); + } else { + appendSummary(buffer, fieldName, (Object[]) value); + } + + } else { + if (detail) { + appendDetail(buffer, fieldName, value); + } else { + appendSummary(buffer, fieldName, value); + } + } + } finally { + unregister(value); + } + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value that has been detected to participate in a cycle. This + * implementation will print the standard string value of the value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code>, + * not <code>null</code> + * + * @since 2.2 + */ + protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) { + ObjectUtils.identityToString(buffer, value); + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value, printing the full detail of the <code>Object</code>.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { + buffer.append(value); + } + + /** + * <p>Append to the <code>toString</code> a <code>Collection</code>.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param coll the <code>Collection</code> to add to the + * <code>toString</code>, not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Collection<?> coll) { + buffer.append(coll); + } + + /** + * <p>Append to the <code>toString</code> a <code>Map<code>.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param map the <code>Map</code> to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Map<?, ?> map) { + buffer.append(map); + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * value, printing a summary of the <code>Object</code>.</P> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, Object value) { + buffer.append(summaryObjectStartText); + buffer.append(getShortClassName(value.getClass())); + buffer.append(summaryObjectEndText); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, long value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, long value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, int value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, int value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, short value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, short value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, byte value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, byte value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, char value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, char value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, double value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, double value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, float value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, float value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param value the value to add to the <code>toString</code> + */ + public void append(StringBuffer buffer, String fieldName, boolean value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * value.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the <code>toString</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) { + buffer.append(value); + } + + /** + * <p>Append to the <code>toString</code> an <code>Object</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> the detail of an + * <code>Object</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + Object item = array[i]; + if (i > 0) { + buffer.append(arraySeparator); + } + if (item == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, item, arrayContentDetail); + } + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> the detail of an array type.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + * @since 2.0 + */ + protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) { + buffer.append(arrayStart); + int length = Array.getLength(array); + for (int i = 0; i < length; i++) { + Object item = Array.get(array, i); + if (i > 0) { + buffer.append(arraySeparator); + } + if (item == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, item, arrayContentDetail); + } + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of an + * <code>Object</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>long</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>long</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>long</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> an <code>int</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of an + * <code>int</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of an + * <code>int</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>short</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>short</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>short</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>byte</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>byte</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>byte</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>char</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the <code>toString</code> + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>char</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>char</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>double</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>double</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, double[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>double</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, double[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>float</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, float[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>float</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, float[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>float</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, float[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> a <code>boolean</code> + * array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail <code>true</code> for detail, <code>false</code> + * for summary info, <code>null</code> for style decides + */ + public void append(StringBuffer buffer, String fieldName, boolean[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + * <p>Append to the <code>toString</code> the detail of a + * <code>boolean</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendDetail(StringBuffer buffer, String fieldName, boolean[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + * <p>Append to the <code>toString</code> a summary of a + * <code>boolean</code> array.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the <code>toString</code>, + * not <code>null</code> + */ + protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + * <p>Append to the <code>toString</code> the class name.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param object the <code>Object</code> whose name to output + */ + protected void appendClassName(StringBuffer buffer, Object object) { + if (useClassName && object != null) { + register(object); + if (useShortClassName) { + buffer.append(getShortClassName(object.getClass())); + } else { + buffer.append(object.getClass().getName()); + } + } + } + + /** + * <p>Append the {@link System#identityHashCode(java.lang.Object)}.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param object the <code>Object</code> whose id to output + */ + protected void appendIdentityHashCode(StringBuffer buffer, Object object) { + if (this.isUseIdentityHashCode() && object!=null) { + register(object); + buffer.append('@'); + buffer.append(Integer.toHexString(System.identityHashCode(object))); + } + } + + /** + * <p>Append to the <code>toString</code> the content start.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + */ + protected void appendContentStart(StringBuffer buffer) { + buffer.append(contentStart); + } + + /** + * <p>Append to the <code>toString</code> the content end.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + */ + protected void appendContentEnd(StringBuffer buffer) { + buffer.append(contentEnd); + } + + /** + * <p>Append to the <code>toString</code> an indicator for <code>null</code>.</p> + * + * <p>The default indicator is <code>'<null>'</code>.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + */ + protected void appendNullText(StringBuffer buffer, String fieldName) { + buffer.append(nullText); + } + + /** + * <p>Append to the <code>toString</code> the field separator.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + */ + protected void appendFieldSeparator(StringBuffer buffer) { + buffer.append(fieldSeparator); + } + + /** + * <p>Append to the <code>toString</code> the field start.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name + */ + protected void appendFieldStart(StringBuffer buffer, String fieldName) { + if (useFieldNames && fieldName != null) { + buffer.append(fieldName); + buffer.append(fieldNameValueSeparator); + } + } + + /** + * <p>Append to the <code>toString<code> the field end.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + */ + protected void appendFieldEnd(StringBuffer buffer, String fieldName) { + appendFieldSeparator(buffer); + } + + /** + * <p>Append to the <code>toString</code> a size summary.</p> + * + * <p>The size summary is used to summarize the contents of + * <code>Collections</code>, <code>Maps</code> and arrays.</p> + * + * <p>The output consists of a prefix, the passed in size + * and a suffix.</p> + * + * <p>The default format is <code>'<size=n>'<code>.</p> + * + * @param buffer the <code>StringBuffer</code> to populate + * @param fieldName the field name, typically not used as already appended + * @param size the size to append + */ + protected void appendSummarySize(StringBuffer buffer, String fieldName, int size) { + buffer.append(sizeStartText); + buffer.append(size); + buffer.append(sizeEndText); + } + + /** + * <p>Is this field to be output in full detail.</p> + * + * <p>This method converts a detail request into a detail level. + * The calling code may request full detail (<code>true</code>), + * but a subclass might ignore that and always return + * <code>false</code>. The calling code may pass in + * <code>null</code> indicating that it doesn't care about + * the detail level. In this case the default detail level is + * used.</p> + * + * @param fullDetailRequest the detail level requested + * @return whether full detail is to be shown + */ + protected boolean isFullDetail(Boolean fullDetailRequest) { + if (fullDetailRequest == null) { + return defaultFullDetail; + } + return fullDetailRequest.booleanValue(); + } + + /** + * <p>Gets the short class name for a class.</p> + * + * <p>The short class name is the classname excluding + * the package name.</p> + * + * @param cls the <code>Class</code> to get the short name of + * @return the short name + */ + protected String getShortClassName(Class<?> cls) { + return ClassUtils.getShortClassName(cls); + } + + // Setters and getters for the customizable parts of the style + // These methods are not expected to be overridden, except to make public + // (They are not public so that immutable subclasses can be written) + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the class name.</p> + * + * @return the current useClassName flag + */ + protected boolean isUseClassName() { + return useClassName; + } + + /** + * <p>Sets whether to use the class name.</p> + * + * @param useClassName the new useClassName flag + */ + protected void setUseClassName(boolean useClassName) { + this.useClassName = useClassName; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to output short or long class names.</p> + * + * @return the current useShortClassName flag + * @since 2.0 + */ + protected boolean isUseShortClassName() { + return useShortClassName; + } + + /** + * <p>Sets whether to output short or long class names.</p> + * + * @param useShortClassName the new useShortClassName flag + * @since 2.0 + */ + protected void setUseShortClassName(boolean useShortClassName) { + this.useShortClassName = useShortClassName; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the identity hash code.</p> + * + * @return the current useIdentityHashCode flag + */ + protected boolean isUseIdentityHashCode() { + return useIdentityHashCode; + } + + /** + * <p>Sets whether to use the identity hash code.</p> + * + * @param useIdentityHashCode the new useIdentityHashCode flag + */ + protected void setUseIdentityHashCode(boolean useIdentityHashCode) { + this.useIdentityHashCode = useIdentityHashCode; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use the field names passed in.</p> + * + * @return the current useFieldNames flag + */ + protected boolean isUseFieldNames() { + return useFieldNames; + } + + /** + * <p>Sets whether to use the field names passed in.</p> + * + * @param useFieldNames the new useFieldNames flag + */ + protected void setUseFieldNames(boolean useFieldNames) { + this.useFieldNames = useFieldNames; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to use full detail when the caller doesn't + * specify.</p> + * + * @return the current defaultFullDetail flag + */ + protected boolean isDefaultFullDetail() { + return defaultFullDetail; + } + + /** + * <p>Sets whether to use full detail when the caller doesn't + * specify.</p> + * + * @param defaultFullDetail the new defaultFullDetail flag + */ + protected void setDefaultFullDetail(boolean defaultFullDetail) { + this.defaultFullDetail = defaultFullDetail; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether to output array content detail.</p> + * + * @return the current array content detail setting + */ + protected boolean isArrayContentDetail() { + return arrayContentDetail; + } + + /** + * <p>Sets whether to output array content detail.</p> + * + * @param arrayContentDetail the new arrayContentDetail flag + */ + protected void setArrayContentDetail(boolean arrayContentDetail) { + this.arrayContentDetail = arrayContentDetail; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array start text.</p> + * + * @return the current array start text + */ + protected String getArrayStart() { + return arrayStart; + } + + /** + * <p>Sets the array start text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param arrayStart the new array start text + */ + protected void setArrayStart(String arrayStart) { + if (arrayStart == null) { + arrayStart = ""; + } + this.arrayStart = arrayStart; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array end text.</p> + * + * @return the current array end text + */ + protected String getArrayEnd() { + return arrayEnd; + } + + /** + * <p>Sets the array end text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param arrayEnd the new array end text + */ + protected void setArrayEnd(String arrayEnd) { + if (arrayEnd == null) { + arrayEnd = ""; + } + this.arrayEnd = arrayEnd; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the array separator text.</p> + * + * @return the current array separator text + */ + protected String getArraySeparator() { + return arraySeparator; + } + + /** + * <p>Sets the array separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param arraySeparator the new array separator text + */ + protected void setArraySeparator(String arraySeparator) { + if (arraySeparator == null) { + arraySeparator = ""; + } + this.arraySeparator = arraySeparator; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the content start text.</p> + * + * @return the current content start text + */ + protected String getContentStart() { + return contentStart; + } + + /** + * <p>Sets the content start text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param contentStart the new content start text + */ + protected void setContentStart(String contentStart) { + if (contentStart == null) { + contentStart = ""; + } + this.contentStart = contentStart; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the content end text.</p> + * + * @return the current content end text + */ + protected String getContentEnd() { + return contentEnd; + } + + /** + * <p>Sets the content end text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param contentEnd the new content end text + */ + protected void setContentEnd(String contentEnd) { + if (contentEnd == null) { + contentEnd = ""; + } + this.contentEnd = contentEnd; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the field name value separator text.</p> + * + * @return the current field name value separator text + */ + protected String getFieldNameValueSeparator() { + return fieldNameValueSeparator; + } + + /** + * <p>Sets the field name value separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param fieldNameValueSeparator the new field name value separator text + */ + protected void setFieldNameValueSeparator(String fieldNameValueSeparator) { + if (fieldNameValueSeparator == null) { + fieldNameValueSeparator = ""; + } + this.fieldNameValueSeparator = fieldNameValueSeparator; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the field separator text.</p> + * + * @return the current field separator text + */ + protected String getFieldSeparator() { + return fieldSeparator; + } + + /** + * <p>Sets the field separator text.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param fieldSeparator the new field separator text + */ + protected void setFieldSeparator(String fieldSeparator) { + if (fieldSeparator == null) { + fieldSeparator = ""; + } + this.fieldSeparator = fieldSeparator; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether the field separator should be added at the start + * of each buffer.</p> + * + * @return the fieldSeparatorAtStart flag + * @since 2.0 + */ + protected boolean isFieldSeparatorAtStart() { + return fieldSeparatorAtStart; + } + + /** + * <p>Sets whether the field separator should be added at the start + * of each buffer.</p> + * + * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag + * @since 2.0 + */ + protected void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { + this.fieldSeparatorAtStart = fieldSeparatorAtStart; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets whether the field separator should be added at the end + * of each buffer.</p> + * + * @return fieldSeparatorAtEnd flag + * @since 2.0 + */ + protected boolean isFieldSeparatorAtEnd() { + return fieldSeparatorAtEnd; + } + + /** + * <p>Sets whether the field separator should be added at the end + * of each buffer.</p> + * + * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag + * @since 2.0 + */ + protected void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { + this.fieldSeparatorAtEnd = fieldSeparatorAtEnd; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the text to output when <code>null</code> found.</p> + * + * @return the current text to output when null found + */ + protected String getNullText() { + return nullText; + } + + /** + * <p>Sets the text to output when <code>null</code> found.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param nullText the new text to output when null found + */ + protected void setNullText(String nullText) { + if (nullText == null) { + nullText = ""; + } + this.nullText = nullText; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the start text to output when a <code>Collection</code>, + * <code>Map</code> or array size is output.</p> + * + * <p>This is output before the size value.</p> + * + * @return the current start of size text + */ + protected String getSizeStartText() { + return sizeStartText; + } + + /** + * <p>Sets the start text to output when a <code>Collection</code>, + * <code>Map</code> or array size is output.</p> + * + * <p>This is output before the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param sizeStartText the new start of size text + */ + protected void setSizeStartText(String sizeStartText) { + if (sizeStartText == null) { + sizeStartText = ""; + } + this.sizeStartText = sizeStartText; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the end text to output when a <code>Collection</code>, + * <code>Map</code> or array size is output.</p> + * + * <p>This is output after the size value.</p> + * + * @return the current end of size text + */ + protected String getSizeEndText() { + return sizeEndText; + } + + /** + * <p>Sets the end text to output when a <code>Collection</code>, + * <code>Map</code> or array size is output.</p> + * + * <p>This is output after the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param sizeEndText the new end of size text + */ + protected void setSizeEndText(String sizeEndText) { + if (sizeEndText == null) { + sizeEndText = ""; + } + this.sizeEndText = sizeEndText; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the start text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output before the size value.</p> + * + * @return the current start of summary text + */ + protected String getSummaryObjectStartText() { + return summaryObjectStartText; + } + + /** + * <p>Sets the start text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output before the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param summaryObjectStartText the new start of summary text + */ + protected void setSummaryObjectStartText(String summaryObjectStartText) { + if (summaryObjectStartText == null) { + summaryObjectStartText = ""; + } + this.summaryObjectStartText = summaryObjectStartText; + } + + //--------------------------------------------------------------------- + + /** + * <p>Gets the end text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output after the size value.</p> + * + * @return the current end of summary text + */ + protected String getSummaryObjectEndText() { + return summaryObjectEndText; + } + + /** + * <p>Sets the end text to output when an <code>Object</code> is + * output in summary mode.</p> + * + * <p>This is output after the size value.</p> + * + * <p><code>null</code> is accepted, but will be converted to + * an empty String.</p> + * + * @param summaryObjectEndText the new end of summary text + */ + protected void setSummaryObjectEndText(String summaryObjectEndText) { + if (summaryObjectEndText == null) { + summaryObjectEndText = ""; + } + this.summaryObjectEndText = summaryObjectEndText; + } + + //---------------------------------------------------------------------------- + + /** + * <p>Default <code>ToStringStyle</code>.</p> + * + * <p>This is an inner class rather than using + * <code>StandardToStringStyle</code> to ensure its immutability.</p> + */ + private static final class DefaultToStringStyle extends ToStringStyle { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + * + * <p>Use the static constant rather than instantiating.</p> + */ + DefaultToStringStyle() { + super(); + } + + /** + * <p>Ensure <code>Singleton</code> after serialization.</p> + * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.DEFAULT_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + * <p><code>ToStringStyle</code> that does not print out + * the field names.</p> + * + * <p>This is an inner class rather than using + * <code>StandardToStringStyle</code> to ensure its immutability. + */ + private static final class NoFieldNameToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + * + * <p>Use the static constant rather than instantiating.</p> + */ + NoFieldNameToStringStyle() { + super(); + this.setUseFieldNames(false); + } + + /** + * <p>Ensure <code>Singleton</code> after serialization.</p> + * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.NO_FIELD_NAMES_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + * <p><code>ToStringStyle</code> that prints out the short + * class name and no identity hashcode.</p> + * + * <p>This is an inner class rather than using + * <code>StandardToStringStyle</code> to ensure its immutability.</p> + */ + private static final class ShortPrefixToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + * + * <p>Use the static constant rather than instantiating.</p> + */ + ShortPrefixToStringStyle() { + super(); + this.setUseShortClassName(true); + this.setUseIdentityHashCode(false); + } + + /** + * <p>Ensure <code>Singleton</ode> after serialization.</p> + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.SHORT_PREFIX_STYLE; + } + + } + + /** + * <p><code>ToStringStyle</code> that does not print out the + * classname, identity hashcode, content start or field name.</p> + * + * <p>This is an inner class rather than using + * <code>StandardToStringStyle</code> to ensure its immutability.</p> + */ + private static final class SimpleToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + * + * <p>Use the static constant rather than instantiating.</p> + */ + SimpleToStringStyle() { + super(); + this.setUseClassName(false); + this.setUseIdentityHashCode(false); + this.setUseFieldNames(false); + this.setContentStart(""); + this.setContentEnd(""); + } + + /** + * <p>Ensure <code>Singleton</ode> after serialization.</p> + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.SIMPLE_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + * <p><code>ToStringStyle</code> that outputs on multiple lines.</p> + * + * <p>This is an inner class rather than using + * <code>StandardToStringStyle</code> to ensure its immutability.</p> + */ + private static final class MultiLineToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + * <p>Constructor.</p> + * + * <p>Use the static constant rather than instantiating.</p> + */ + MultiLineToStringStyle() { + super(); + this.setContentStart("["); + this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " "); + this.setFieldSeparatorAtStart(true); + this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]"); + } + + /** + * <p>Ensure <code>Singleton</code> after serialization.</p> + * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.MULTI_LINE_STYLE; + } + + } + +} diff --git a/src/org/apache/commons/lang3/exception/CloneFailedException.java b/src/org/apache/commons/lang3/exception/CloneFailedException.java index 0c324e9..138b250 100644 --- a/src/org/apache/commons/lang3/exception/CloneFailedException.java +++ b/src/org/apache/commons/lang3/exception/CloneFailedException.java @@ -1,62 +1,62 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-/**
- * Exception thrown when a clone cannot be created. In contrast to
- * {@link CloneNotSupportedException} this is a {@link RuntimeException}.
- *
- * @since 3.0
- */
-public class CloneFailedException extends RuntimeException {
- // ~ Static fields/initializers ---------------------------------------------
-
- private static final long serialVersionUID = 20091223L;
-
- // ~ Constructors -----------------------------------------------------------
-
- /**
- * Constructs a CloneFailedException.
- *
- * @param message description of the exception
- * @since upcoming
- */
- public CloneFailedException(final String message) {
- super(message);
- }
-
- /**
- * Constructs a CloneFailedException.
- *
- * @param cause cause of the exception
- * @since upcoming
- */
- public CloneFailedException(final Throwable cause) {
- super(cause);
- }
-
- /**
- * Constructs a CloneFailedException.
- *
- * @param message description of the exception
- * @param cause cause of the exception
- * @since upcoming
- */
- public CloneFailedException(final String message, final Throwable cause) {
- super(message, cause);
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +/** + * Exception thrown when a clone cannot be created. In contrast to + * {@link CloneNotSupportedException} this is a {@link RuntimeException}. + * + * @since 3.0 + */ +public class CloneFailedException extends RuntimeException { + // ~ Static fields/initializers --------------------------------------------- + + private static final long serialVersionUID = 20091223L; + + // ~ Constructors ----------------------------------------------------------- + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @since upcoming + */ + public CloneFailedException(final String message) { + super(message); + } + + /** + * Constructs a CloneFailedException. + * + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/src/org/apache/commons/lang3/exception/ContextedException.java b/src/org/apache/commons/lang3/exception/ContextedException.java index cd517e0..a910024 100644 --- a/src/org/apache/commons/lang3/exception/ContextedException.java +++ b/src/org/apache/commons/lang3/exception/ContextedException.java @@ -1,246 +1,246 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * <p>
- * An exception that provides an easy and safe way to add contextual information.
- * </p><p>
- * An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
- * Frequently what is needed is a select few pieces of local contextual data.
- * Providing this data is tricky however, due to concerns over formatting and nulls.
- * </p><p>
- * The contexted exception approach allows the exception to be created together with a
- * list of context label-value pairs. This additional information is automatically included in
- * the message and printed stack trace.
- * </p><p>
- * An unchecked version of this exception is provided by ContextedRuntimeException.
- * </p>
- * <p>
- * To use this class write code as follows:
- * </p>
- * <pre>
- * try {
- * ...
- * } catch (Exception e) {
- * throw new ContextedException("Error posting account transaction", e)
- * .addContextValue("Account Number", accountNumber)
- * .addContextValue("Amount Posted", amountPosted)
- * .addContextValue("Previous Balance", previousBalance)
- * }
- * }
- * </pre> or improve diagnose data at a higher level:
- * <pre>
- * try {
- * ...
- * } catch (ContextedException e) {
- * throw e.setContextValue("Transaction Id", transactionId);
- * } catch (Exception e) {
- * if (e instanceof ExceptionContext) {
- * e.setContextValue("Transaction Id", transactionId);
- * }
- * throw e;
- * }
- * }
- * </pre>
- * </p><p>
- * The output in a printStacktrace() (which often is written to a log) would look something like the following:
- * <pre>
- * org.apache.commons.lang3.exception.ContextedException: java.lang.Exception: Error posting account transaction
- * Exception Context:
- * [1:Account Number=null]
- * [2:Amount Posted=100.00]
- * [3:Previous Balance=-2.17]
- * [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
- *
- * ---------------------------------
- * at org.apache.commons.lang3.exception.ContextedExceptionTest.testAddValue(ContextedExceptionTest.java:88)
- * ..... (rest of trace)
- * </pre>
- * </p>
- *
- * @see ContextedRuntimeException
- * @since 3.0
- */
-public class ContextedException extends Exception implements ExceptionContext {
-
- /** The serialization version. */
- private static final long serialVersionUID = 20110706L;
- /** The context where the data is stored. */
- private final ExceptionContext exceptionContext;
-
- /**
- * Instantiates ContextedException without message or cause.
- * <p>
- * The context information is stored using a default implementation.
- */
- public ContextedException() {
- super();
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedException with message, but without cause.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param message the exception message, may be null
- */
- public ContextedException(String message) {
- super(message);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedException with cause, but without message.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param cause the underlying cause of the exception, may be null
- */
- public ContextedException(Throwable cause) {
- super(cause);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedException with cause and message.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param message the exception message, may be null
- * @param cause the underlying cause of the exception, may be null
- */
- public ContextedException(String message, Throwable cause) {
- super(message, cause);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedException with cause, message, and ExceptionContext.
- *
- * @param message the exception message, may be null
- * @param cause the underlying cause of the exception, may be null
- * @param context the context used to store the additional information, null uses default implementation
- */
- public ContextedException(String message, Throwable cause, ExceptionContext context) {
- super(message, cause);
- if (context == null) {
- context = new DefaultExceptionContext();
- }
- exceptionContext = context;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds information helpful to a developer in diagnosing and correcting the problem.
- * For the information to be meaningful, the value passed should have a reasonable
- * toString() implementation.
- * Different values can be added with the same label multiple times.
- * <p>
- * Note: This exception is only serializable if the object added is serializable.
- * </p>
- *
- * @param label a textual label associated with information, {@code null} not recommended
- * @param value information needed to understand exception, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ContextedException addContextValue(String label, Object value) {
- exceptionContext.addContextValue(label, value);
- return this;
- }
-
- /**
- * Sets information helpful to a developer in diagnosing and correcting the problem.
- * For the information to be meaningful, the value passed should have a reasonable
- * toString() implementation.
- * Any existing values with the same labels are removed before the new one is added.
- * <p>
- * Note: This exception is only serializable if the object added as value is serializable.
- * </p>
- *
- * @param label a textual label associated with information, {@code null} not recommended
- * @param value information needed to understand exception, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ContextedException setContextValue(String label, Object value) {
- exceptionContext.setContextValue(label, value);
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Object> getContextValues(String label) {
- return this.exceptionContext.getContextValues(label);
- }
-
- /**
- * {@inheritDoc}
- */
- public Object getFirstContextValue(String label) {
- return this.exceptionContext.getFirstContextValue(label);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Pair<String, Object>> getContextEntries() {
- return this.exceptionContext.getContextEntries();
- }
-
- /**
- * {@inheritDoc}
- */
- public Set<String> getContextLabels() {
- return exceptionContext.getContextLabels();
- }
-
- /**
- * Provides the message explaining the exception, including the contextual data.
- *
- * @see java.lang.Throwable#getMessage()
- * @return the message, never null
- */
- @Override
- public String getMessage(){
- return getFormattedExceptionMessage(super.getMessage());
- }
-
- /**
- * Provides the message explaining the exception without the contextual data.
- *
- * @see java.lang.Throwable#getMessage()
- * @return the message
- * @since 3.0.1
- */
- public String getRawMessage() {
- return super.getMessage();
- }
-
- /**
- * {@inheritDoc}
- */
- public String getFormattedExceptionMessage(String baseMessage) {
- return exceptionContext.getFormattedExceptionMessage(baseMessage);
- }
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * <p> + * An exception that provides an easy and safe way to add contextual information. + * </p><p> + * An exception trace itself is often insufficient to provide rapid diagnosis of the issue. + * Frequently what is needed is a select few pieces of local contextual data. + * Providing this data is tricky however, due to concerns over formatting and nulls. + * </p><p> + * The contexted exception approach allows the exception to be created together with a + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. + * </p><p> + * An unchecked version of this exception is provided by ContextedRuntimeException. + * </p> + * <p> + * To use this class write code as follows: + * </p> + * <pre> + * try { + * ... + * } catch (Exception e) { + * throw new ContextedException("Error posting account transaction", e) + * .addContextValue("Account Number", accountNumber) + * .addContextValue("Amount Posted", amountPosted) + * .addContextValue("Previous Balance", previousBalance) + * } + * } + * </pre> or improve diagnose data at a higher level: + * <pre> + * try { + * ... + * } catch (ContextedException e) { + * throw e.setContextValue("Transaction Id", transactionId); + * } catch (Exception e) { + * if (e instanceof ExceptionContext) { + * e.setContextValue("Transaction Id", transactionId); + * } + * throw e; + * } + * } + * </pre> + * </p><p> + * The output in a printStacktrace() (which often is written to a log) would look something like the following: + * <pre> + * org.apache.commons.lang3.exception.ContextedException: java.lang.Exception: Error posting account transaction + * Exception Context: + * [1:Account Number=null] + * [2:Amount Posted=100.00] + * [3:Previous Balance=-2.17] + * [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899] + * + * --------------------------------- + * at org.apache.commons.lang3.exception.ContextedExceptionTest.testAddValue(ContextedExceptionTest.java:88) + * ..... (rest of trace) + * </pre> + * </p> + * + * @see ContextedRuntimeException + * @since 3.0 + */ +public class ContextedException extends Exception implements ExceptionContext { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + /** The context where the data is stored. */ + private final ExceptionContext exceptionContext; + + /** + * Instantiates ContextedException without message or cause. + * <p> + * The context information is stored using a default implementation. + */ + public ContextedException() { + super(); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with message, but without cause. + * <p> + * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + */ + public ContextedException(String message) { + super(message); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause, but without message. + * <p> + * The context information is stored using a default implementation. + * + * @param cause the underlying cause of the exception, may be null + */ + public ContextedException(Throwable cause) { + super(cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause and message. + * <p> + * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + */ + public ContextedException(String message, Throwable cause) { + super(message, cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause, message, and ExceptionContext. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + * @param context the context used to store the additional information, null uses default implementation + */ + public ContextedException(String message, Throwable cause, ExceptionContext context) { + super(message, cause); + if (context == null) { + context = new DefaultExceptionContext(); + } + exceptionContext = context; + } + + //----------------------------------------------------------------------- + /** + * Adds information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Different values can be added with the same label multiple times. + * <p> + * Note: This exception is only serializable if the object added is serializable. + * </p> + * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ContextedException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); + return this; + } + + /** + * Sets information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Any existing values with the same labels are removed before the new one is added. + * <p> + * Note: This exception is only serializable if the object added as value is serializable. + * </p> + * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ContextedException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + public List<Object> getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + public List<Pair<String, Object>> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + public Set<String> getContextLabels() { + return exceptionContext.getContextLabels(); + } + + /** + * Provides the message explaining the exception, including the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message, never null + */ + @Override + public String getMessage(){ + return getFormattedExceptionMessage(super.getMessage()); + } + + /** + * Provides the message explaining the exception without the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message + * @since 3.0.1 + */ + public String getRawMessage() { + return super.getMessage(); + } + + /** + * {@inheritDoc} + */ + public String getFormattedExceptionMessage(String baseMessage) { + return exceptionContext.getFormattedExceptionMessage(baseMessage); + } +} diff --git a/src/org/apache/commons/lang3/exception/ContextedRuntimeException.java b/src/org/apache/commons/lang3/exception/ContextedRuntimeException.java index 85e035e..c8e2a29 100644 --- a/src/org/apache/commons/lang3/exception/ContextedRuntimeException.java +++ b/src/org/apache/commons/lang3/exception/ContextedRuntimeException.java @@ -1,247 +1,247 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * <p>
- * A runtime exception that provides an easy and safe way to add contextual information.
- * </p><p>
- * An exception trace itself is often insufficient to provide rapid diagnosis of the issue.
- * Frequently what is needed is a select few pieces of local contextual data.
- * Providing this data is tricky however, due to concerns over formatting and nulls.
- * </p><p>
- * The contexted exception approach allows the exception to be created together with a
- * list of context label-value pairs. This additional information is automatically included in
- * the message and printed stack trace.
- * </p><p>
- * A checked version of this exception is provided by ContextedException.
- * </p>
- * <p>
- * To use this class write code as follows:
- * </p>
- * <pre>
- * try {
- * ...
- * } catch (Exception e) {
- * throw new ContextedRuntimeException("Error posting account transaction", e)
- * .addContextValue("Account Number", accountNumber)
- * .addContextValue("Amount Posted", amountPosted)
- * .addContextValue("Previous Balance", previousBalance)
- * }
- * }
- * </pre> or improve diagnose data at a higher level:
- * <pre>
- * try {
- * ...
- * } catch (ContextedRuntimeException e) {
- * throw e.setContextValue("Transaction Id", transactionId);
- * } catch (Exception e) {
- * if (e instanceof ExceptionContext) {
- * e.setContextValue("Transaction Id", transactionId);
- * }
- * throw e;
- * }
- * }
- * </pre>
- * </p><p>
- * The output in a printStacktrace() (which often is written to a log) would look something like the following:
- * <pre>
- * org.apache.commons.lang3.exception.ContextedRuntimeException: java.lang.Exception: Error posting account transaction
- * Exception Context:
- * [1:Account Number=null]
- * [2:Amount Posted=100.00]
- * [3:Previous Balance=-2.17]
- * [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
- *
- * ---------------------------------
- * at org.apache.commons.lang3.exception.ContextedRuntimeExceptionTest.testAddValue(ContextedExceptionTest.java:88)
- * ..... (rest of trace)
- * </pre>
- * </p>
- *
- * @see ContextedException
- * @since 3.0
- */
-public class ContextedRuntimeException extends RuntimeException implements ExceptionContext {
-
- /** The serialization version. */
- private static final long serialVersionUID = 20110706L;
- /** The context where the data is stored. */
- private final ExceptionContext exceptionContext;
-
- /**
- * Instantiates ContextedRuntimeException without message or cause.
- * <p>
- * The context information is stored using a default implementation.
- */
- public ContextedRuntimeException() {
- super();
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedRuntimeException with message, but without cause.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param message the exception message, may be null
- */
- public ContextedRuntimeException(String message) {
- super(message);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedRuntimeException with cause, but without message.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param cause the underlying cause of the exception, may be null
- */
- public ContextedRuntimeException(Throwable cause) {
- super(cause);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedRuntimeException with cause and message.
- * <p>
- * The context information is stored using a default implementation.
- *
- * @param message the exception message, may be null
- * @param cause the underlying cause of the exception, may be null
- */
- public ContextedRuntimeException(String message, Throwable cause) {
- super(message, cause);
- exceptionContext = new DefaultExceptionContext();
- }
-
- /**
- * Instantiates ContextedRuntimeException with cause, message, and ExceptionContext.
- *
- * @param message the exception message, may be null
- * @param cause the underlying cause of the exception, may be null
- * @param context the context used to store the additional information, null uses default implementation
- */
- public ContextedRuntimeException(String message, Throwable cause, ExceptionContext context) {
- super(message, cause);
- if (context == null) {
- context = new DefaultExceptionContext();
- }
- exceptionContext = context;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds information helpful to a developer in diagnosing and correcting the problem.
- * For the information to be meaningful, the value passed should have a reasonable
- * toString() implementation.
- * Different values can be added with the same label multiple times.
- * <p>
- * Note: This exception is only serializable if the object added is serializable.
- * </p>
- *
- * @param label a textual label associated with information, {@code null} not recommended
- * @param value information needed to understand exception, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ContextedRuntimeException addContextValue(String label, Object value) {
- exceptionContext.addContextValue(label, value);
- return this;
- }
-
- /**
- * Sets information helpful to a developer in diagnosing and correcting the problem.
- * For the information to be meaningful, the value passed should have a reasonable
- * toString() implementation.
- * Any existing values with the same labels are removed before the new one is added.
- * <p>
- * Note: This exception is only serializable if the object added as value is serializable.
- * </p>
- *
- * @param label a textual label associated with information, {@code null} not recommended
- * @param value information needed to understand exception, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ContextedRuntimeException setContextValue(String label, Object value) {
- exceptionContext.setContextValue(label, value);
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Object> getContextValues(String label) {
- return this.exceptionContext.getContextValues(label);
- }
-
- /**
- * {@inheritDoc}
- */
- public Object getFirstContextValue(String label) {
- return this.exceptionContext.getFirstContextValue(label);
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Pair<String, Object>> getContextEntries() {
- return this.exceptionContext.getContextEntries();
- }
-
- /**
- * {@inheritDoc}
- */
- public Set<String> getContextLabels() {
- return exceptionContext.getContextLabels();
- }
-
- /**
- * Provides the message explaining the exception, including the contextual data.
- *
- * @see java.lang.Throwable#getMessage()
- * @return the message, never null
- */
- @Override
- public String getMessage(){
- return getFormattedExceptionMessage(super.getMessage());
- }
-
- /**
- * Provides the message explaining the exception without the contextual data.
- *
- * @see java.lang.Throwable#getMessage()
- * @return the message
- * @since 3.0.1
- */
- public String getRawMessage() {
- return super.getMessage();
- }
-
- /**
- * {@inheritDoc}
- */
- public String getFormattedExceptionMessage(String baseMessage) {
- return exceptionContext.getFormattedExceptionMessage(baseMessage);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * <p> + * A runtime exception that provides an easy and safe way to add contextual information. + * </p><p> + * An exception trace itself is often insufficient to provide rapid diagnosis of the issue. + * Frequently what is needed is a select few pieces of local contextual data. + * Providing this data is tricky however, due to concerns over formatting and nulls. + * </p><p> + * The contexted exception approach allows the exception to be created together with a + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. + * </p><p> + * A checked version of this exception is provided by ContextedException. + * </p> + * <p> + * To use this class write code as follows: + * </p> + * <pre> + * try { + * ... + * } catch (Exception e) { + * throw new ContextedRuntimeException("Error posting account transaction", e) + * .addContextValue("Account Number", accountNumber) + * .addContextValue("Amount Posted", amountPosted) + * .addContextValue("Previous Balance", previousBalance) + * } + * } + * </pre> or improve diagnose data at a higher level: + * <pre> + * try { + * ... + * } catch (ContextedRuntimeException e) { + * throw e.setContextValue("Transaction Id", transactionId); + * } catch (Exception e) { + * if (e instanceof ExceptionContext) { + * e.setContextValue("Transaction Id", transactionId); + * } + * throw e; + * } + * } + * </pre> + * </p><p> + * The output in a printStacktrace() (which often is written to a log) would look something like the following: + * <pre> + * org.apache.commons.lang3.exception.ContextedRuntimeException: java.lang.Exception: Error posting account transaction + * Exception Context: + * [1:Account Number=null] + * [2:Amount Posted=100.00] + * [3:Previous Balance=-2.17] + * [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899] + * + * --------------------------------- + * at org.apache.commons.lang3.exception.ContextedRuntimeExceptionTest.testAddValue(ContextedExceptionTest.java:88) + * ..... (rest of trace) + * </pre> + * </p> + * + * @see ContextedException + * @since 3.0 + */ +public class ContextedRuntimeException extends RuntimeException implements ExceptionContext { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + /** The context where the data is stored. */ + private final ExceptionContext exceptionContext; + + /** + * Instantiates ContextedRuntimeException without message or cause. + * <p> + * The context information is stored using a default implementation. + */ + public ContextedRuntimeException() { + super(); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with message, but without cause. + * <p> + * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + */ + public ContextedRuntimeException(String message) { + super(message); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause, but without message. + * <p> + * The context information is stored using a default implementation. + * + * @param cause the underlying cause of the exception, may be null + */ + public ContextedRuntimeException(Throwable cause) { + super(cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause and message. + * <p> + * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + */ + public ContextedRuntimeException(String message, Throwable cause) { + super(message, cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause, message, and ExceptionContext. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + * @param context the context used to store the additional information, null uses default implementation + */ + public ContextedRuntimeException(String message, Throwable cause, ExceptionContext context) { + super(message, cause); + if (context == null) { + context = new DefaultExceptionContext(); + } + exceptionContext = context; + } + + //----------------------------------------------------------------------- + /** + * Adds information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Different values can be added with the same label multiple times. + * <p> + * Note: This exception is only serializable if the object added is serializable. + * </p> + * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ContextedRuntimeException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); + return this; + } + + /** + * Sets information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Any existing values with the same labels are removed before the new one is added. + * <p> + * Note: This exception is only serializable if the object added as value is serializable. + * </p> + * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ContextedRuntimeException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + public List<Object> getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + public List<Pair<String, Object>> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + public Set<String> getContextLabels() { + return exceptionContext.getContextLabels(); + } + + /** + * Provides the message explaining the exception, including the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message, never null + */ + @Override + public String getMessage(){ + return getFormattedExceptionMessage(super.getMessage()); + } + + /** + * Provides the message explaining the exception without the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message + * @since 3.0.1 + */ + public String getRawMessage() { + return super.getMessage(); + } + + /** + * {@inheritDoc} + */ + public String getFormattedExceptionMessage(String baseMessage) { + return exceptionContext.getFormattedExceptionMessage(baseMessage); + } + +} diff --git a/src/org/apache/commons/lang3/exception/DefaultExceptionContext.java b/src/org/apache/commons/lang3/exception/DefaultExceptionContext.java index b8128ec..fc5dc52 100644 --- a/src/org/apache/commons/lang3/exception/DefaultExceptionContext.java +++ b/src/org/apache/commons/lang3/exception/DefaultExceptionContext.java @@ -1,158 +1,158 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * Default implementation of the context storing the label-value pairs for contexted exceptions.
- * <p>
- * This implementation is serializable, however this is dependent on the values that
- * are added also being serializable.
- * </p>
- *
- * @see ContextedException
- * @see ContextedRuntimeException
- * @since 3.0
- */
-public class DefaultExceptionContext implements ExceptionContext, Serializable {
-
- /** The serialization version. */
- private static final long serialVersionUID = 20110706L;
-
- /** The list storing the label-data pairs. */
- private final List<Pair<String, Object>> contextValues = new ArrayList<Pair<String,Object>>();
-
- /**
- * {@inheritDoc}
- */
- public DefaultExceptionContext addContextValue(String label, Object value) {
- contextValues.add(new ImmutablePair<String, Object>(label, value));
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- public DefaultExceptionContext setContextValue(String label, Object value) {
- for (final Iterator<Pair<String, Object>> iter = contextValues.iterator(); iter.hasNext();) {
- final Pair<String, Object> p = iter.next();
- if (StringUtils.equals(label, p.getKey())) {
- iter.remove();
- }
- }
- addContextValue(label, value);
- return this;
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Object> getContextValues(String label) {
- final List<Object> values = new ArrayList<Object>();
- for (final Pair<String, Object> pair : contextValues) {
- if (StringUtils.equals(label, pair.getKey())) {
- values.add(pair.getValue());
- }
- }
- return values;
- }
-
- /**
- * {@inheritDoc}
- */
- public Object getFirstContextValue(String label) {
- for (final Pair<String, Object> pair : contextValues) {
- if (StringUtils.equals(label, pair.getKey())) {
- return pair.getValue();
- }
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- public Set<String> getContextLabels() {
- final Set<String> labels = new HashSet<String>();
- for (final Pair<String, Object> pair : contextValues) {
- labels.add(pair.getKey());
- }
- return labels;
- }
-
- /**
- * {@inheritDoc}
- */
- public List<Pair<String, Object>> getContextEntries() {
- return contextValues;
- }
-
- /**
- * Builds the message containing the contextual information.
- *
- * @param baseMessage the base exception message <b>without</b> context information appended
- * @return the exception message <b>with</b> context information appended, never null
- */
- public String getFormattedExceptionMessage(String baseMessage){
- StringBuilder buffer = new StringBuilder(256);
- if (baseMessage != null) {
- buffer.append(baseMessage);
- }
-
- if (contextValues.size() > 0) {
- if (buffer.length() > 0) {
- buffer.append('\n');
- }
- buffer.append("Exception Context:\n");
-
- int i = 0;
- for (final Pair<String, Object> pair : contextValues) {
- buffer.append("\t[");
- buffer.append(++i);
- buffer.append(':');
- buffer.append(pair.getKey());
- buffer.append("=");
- final Object value = pair.getValue();
- if (value == null) {
- buffer.append("null");
- } else {
- String valueStr;
- try {
- valueStr = value.toString();
- } catch (Exception e) {
- valueStr = "Exception thrown on toString(): " + ExceptionUtils.getStackTrace(e);
- }
- buffer.append(valueStr);
- }
- buffer.append("]\n");
- }
- buffer.append("---------------------------------");
- }
- return buffer.toString();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Default implementation of the context storing the label-value pairs for contexted exceptions. + * <p> + * This implementation is serializable, however this is dependent on the values that + * are added also being serializable. + * </p> + * + * @see ContextedException + * @see ContextedRuntimeException + * @since 3.0 + */ +public class DefaultExceptionContext implements ExceptionContext, Serializable { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + + /** The list storing the label-data pairs. */ + private final List<Pair<String, Object>> contextValues = new ArrayList<Pair<String,Object>>(); + + /** + * {@inheritDoc} + */ + public DefaultExceptionContext addContextValue(String label, Object value) { + contextValues.add(new ImmutablePair<String, Object>(label, value)); + return this; + } + + /** + * {@inheritDoc} + */ + public DefaultExceptionContext setContextValue(String label, Object value) { + for (final Iterator<Pair<String, Object>> iter = contextValues.iterator(); iter.hasNext();) { + final Pair<String, Object> p = iter.next(); + if (StringUtils.equals(label, p.getKey())) { + iter.remove(); + } + } + addContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + public List<Object> getContextValues(String label) { + final List<Object> values = new ArrayList<Object>(); + for (final Pair<String, Object> pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + values.add(pair.getValue()); + } + } + return values; + } + + /** + * {@inheritDoc} + */ + public Object getFirstContextValue(String label) { + for (final Pair<String, Object> pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + return pair.getValue(); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + public Set<String> getContextLabels() { + final Set<String> labels = new HashSet<String>(); + for (final Pair<String, Object> pair : contextValues) { + labels.add(pair.getKey()); + } + return labels; + } + + /** + * {@inheritDoc} + */ + public List<Pair<String, Object>> getContextEntries() { + return contextValues; + } + + /** + * Builds the message containing the contextual information. + * + * @param baseMessage the base exception message <b>without</b> context information appended + * @return the exception message <b>with</b> context information appended, never null + */ + public String getFormattedExceptionMessage(String baseMessage){ + StringBuilder buffer = new StringBuilder(256); + if (baseMessage != null) { + buffer.append(baseMessage); + } + + if (contextValues.size() > 0) { + if (buffer.length() > 0) { + buffer.append('\n'); + } + buffer.append("Exception Context:\n"); + + int i = 0; + for (final Pair<String, Object> pair : contextValues) { + buffer.append("\t["); + buffer.append(++i); + buffer.append(':'); + buffer.append(pair.getKey()); + buffer.append("="); + final Object value = pair.getValue(); + if (value == null) { + buffer.append("null"); + } else { + String valueStr; + try { + valueStr = value.toString(); + } catch (Exception e) { + valueStr = "Exception thrown on toString(): " + ExceptionUtils.getStackTrace(e); + } + buffer.append(valueStr); + } + buffer.append("]\n"); + } + buffer.append("---------------------------------"); + } + return buffer.toString(); + } + +} diff --git a/src/org/apache/commons/lang3/exception/ExceptionContext.java b/src/org/apache/commons/lang3/exception/ExceptionContext.java index d8076c8..719dad5 100644 --- a/src/org/apache/commons/lang3/exception/ExceptionContext.java +++ b/src/org/apache/commons/lang3/exception/ExceptionContext.java @@ -1,103 +1,103 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-import java.util.List;
-import java.util.Set;
-
-import org.apache.commons.lang3.tuple.Pair;
-
-/**
- * Allows the storage and retrieval of contextual information based on label-value
- * pairs for exceptions.
- * <p>
- * Implementations are expected to manage the pairs in a list-style collection
- * that keeps the pairs in the sequence of their addition.
- * </p>
- *
- * @see ContextedException
- * @see ContextedRuntimeException
- * @since 3.0
- */
-public interface ExceptionContext {
-
- /**
- * Adds a contextual label-value pair into this context.
- * <p>
- * The pair will be added to the context, independently of an already
- * existing pair with the same label.
- * </p>
- *
- * @param label the label of the item to add, {@code null} not recommended
- * @param value the value of item to add, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ExceptionContext addContextValue(String label, Object value);
-
- /**
- * Sets a contextual label-value pair into this context.
- * <p>
- * The pair will be added normally, but any existing label-value pair with
- * the same label is removed from the context.
- * </p>
- *
- * @param label the label of the item to add, {@code null} not recommended
- * @param value the value of item to add, may be {@code null}
- * @return {@code this}, for method chaining, not {@code null}
- */
- public ExceptionContext setContextValue(String label, Object value);
-
- /**
- * Retrieves all the contextual data values associated with the label.
- *
- * @param label the label to get the contextual values for, may be {@code null}
- * @return the contextual values associated with the label, never {@code null}
- */
- public List<Object> getContextValues(String label);
-
- /**
- * Retrieves the first available contextual data value associated with the label.
- *
- * @param label the label to get the contextual value for, may be {@code null}
- * @return the first contextual value associated with the label, may be {@code null}
- */
- public Object getFirstContextValue(String label);
-
- /**
- * Retrieves the full set of labels defined in the contextual data.
- *
- * @return the set of labels, not {@code null}
- */
- public Set<String> getContextLabels();
-
- /**
- * Retrieves the full list of label-value pairs defined in the contextual data.
- *
- * @return the list of pairs, not {@code null}
- */
- public List<Pair<String, Object>> getContextEntries();
-
- /**
- * Gets the contextualized error message based on a base message.
- * This will add the context label-value pairs to the message.
- *
- * @param baseMessage the base exception message <b>without</b> context information appended
- * @return the exception message <b>with</b> context information appended, not {@code null}
- */
- public String getFormattedExceptionMessage(String baseMessage);
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * Allows the storage and retrieval of contextual information based on label-value + * pairs for exceptions. + * <p> + * Implementations are expected to manage the pairs in a list-style collection + * that keeps the pairs in the sequence of their addition. + * </p> + * + * @see ContextedException + * @see ContextedRuntimeException + * @since 3.0 + */ +public interface ExceptionContext { + + /** + * Adds a contextual label-value pair into this context. + * <p> + * The pair will be added to the context, independently of an already + * existing pair with the same label. + * </p> + * + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ExceptionContext addContextValue(String label, Object value); + + /** + * Sets a contextual label-value pair into this context. + * <p> + * The pair will be added normally, but any existing label-value pair with + * the same label is removed from the context. + * </p> + * + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ExceptionContext setContextValue(String label, Object value); + + /** + * Retrieves all the contextual data values associated with the label. + * + * @param label the label to get the contextual values for, may be {@code null} + * @return the contextual values associated with the label, never {@code null} + */ + public List<Object> getContextValues(String label); + + /** + * Retrieves the first available contextual data value associated with the label. + * + * @param label the label to get the contextual value for, may be {@code null} + * @return the first contextual value associated with the label, may be {@code null} + */ + public Object getFirstContextValue(String label); + + /** + * Retrieves the full set of labels defined in the contextual data. + * + * @return the set of labels, not {@code null} + */ + public Set<String> getContextLabels(); + + /** + * Retrieves the full list of label-value pairs defined in the contextual data. + * + * @return the list of pairs, not {@code null} + */ + public List<Pair<String, Object>> getContextEntries(); + + /** + * Gets the contextualized error message based on a base message. + * This will add the context label-value pairs to the message. + * + * @param baseMessage the base exception message <b>without</b> context information appended + * @return the exception message <b>with</b> context information appended, not {@code null} + */ + public String getFormattedExceptionMessage(String baseMessage); + +} diff --git a/src/org/apache/commons/lang3/exception/ExceptionUtils.java b/src/org/apache/commons/lang3/exception/ExceptionUtils.java index 68d3e53..9adfcd9 100644 --- a/src/org/apache/commons/lang3/exception/ExceptionUtils.java +++ b/src/org/apache/commons/lang3/exception/ExceptionUtils.java @@ -1,697 +1,697 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.exception;
-
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.StringTokenizer;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.apache.commons.lang3.ClassUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.SystemUtils;
-
-/**
- * <p>Provides utilities for manipulating and examining
- * <code>Throwable</code> objects.</p>
- *
- * @since 1.0
- * @version $Id: ExceptionUtils.java 1144929 2011-07-10 18:26:16Z ggregory $
- */
-public class ExceptionUtils {
-
- /**
- * <p>Used when printing stack frames to denote the start of a
- * wrapped exception.</p>
- *
- * <p>Package private for accessibility by test suite.</p>
- */
- static final String WRAPPED_MARKER = " [wrapped] ";
-
- /**
- * <p>The names of methods commonly used to access a wrapped exception.</p>
- */
- // TODO: Remove in Lang 4.0
- private static final String[] CAUSE_METHOD_NAMES = {
- "getCause",
- "getNextException",
- "getTargetException",
- "getException",
- "getSourceException",
- "getRootCause",
- "getCausedByException",
- "getNested",
- "getLinkedException",
- "getNestedException",
- "getLinkedCause",
- "getThrowable",
- };
-
- /**
- * <p>
- * Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not
- * normally necessary.
- * </p>
- */
- public ExceptionUtils() {
- super();
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns the default names used when searching for the cause of an exception.</p>
- *
- * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p>
- *
- * @return cloned array of the default method names
- * @since 3.0
- * @deprecated This feature will be removed in Lang 4.0
- */
- @Deprecated
- public static String[] getDefaultCauseMethodNames() {
- return ArrayUtils.clone(CAUSE_METHOD_NAMES);
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
- *
- * <p>The method searches for methods with specific names that return a
- * <code>Throwable</code> object. This will pick up most wrapping exceptions,
- * including those from JDK 1.4.
- *
- * <p>The default list searched for are:</p>
- * <ul>
- * <li><code>getCause()</code></li>
- * <li><code>getNextException()</code></li>
- * <li><code>getTargetException()</code></li>
- * <li><code>getException()</code></li>
- * <li><code>getSourceException()</code></li>
- * <li><code>getRootCause()</code></li>
- * <li><code>getCausedByException()</code></li>
- * <li><code>getNested()</code></li>
- * </ul>
- *
- * <p>If none of the above is found, returns <code>null</code>.</p>
- *
- * @param throwable the throwable to introspect for a cause, may be null
- * @return the cause of the <code>Throwable</code>,
- * <code>null</code> if none found or null throwable input
- * @since 1.0
- * @deprecated This feature will be removed in Lang 4.0
- */
- @Deprecated
- public static Throwable getCause(Throwable throwable) {
- return getCause(throwable, CAUSE_METHOD_NAMES);
- }
-
- /**
- * <p>Introspects the <code>Throwable</code> to obtain the cause.</p>
- *
- * <p>A <code>null</code> set of method names means use the default set.
- * A <code>null</code> in the set of method names will be ignored.</p>
- *
- * @param throwable the throwable to introspect for a cause, may be null
- * @param methodNames the method names, null treated as default set
- * @return the cause of the <code>Throwable</code>,
- * <code>null</code> if none found or null throwable input
- * @since 1.0
- * @deprecated This feature will be removed in Lang 4.0
- */
- @Deprecated
- public static Throwable getCause(Throwable throwable, String[] methodNames) {
- if (throwable == null) {
- return null;
- }
-
- if (methodNames == null) {
- methodNames = CAUSE_METHOD_NAMES;
- }
-
- for (String methodName : methodNames) {
- if (methodName != null) {
- Throwable cause = getCauseUsingMethodName(throwable, methodName);
- if (cause != null) {
- return cause;
- }
- }
- }
-
- return null;
- }
-
- /**
- * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p>
- *
- * <p>This method walks through the exception chain to the last element,
- * "root" of the tree, using {@link #getCause(Throwable)}, and
- * returns that exception.</p>
- *
- * <p>From version 2.2, this method handles recursive cause structures
- * that might otherwise cause infinite loops. If the throwable parameter
- * has a cause of itself, then null will be returned. If the throwable
- * parameter cause chain loops, the last element in the chain before the
- * loop is returned.</p>
- *
- * @param throwable the throwable to get the root cause for, may be null
- * @return the root cause of the <code>Throwable</code>,
- * <code>null</code> if none found or null throwable input
- */
- public static Throwable getRootCause(Throwable throwable) {
- List<Throwable> list = getThrowableList(throwable);
- return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1));
- }
-
- /**
- * <p>Finds a <code>Throwable</code> by method name.</p>
- *
- * @param throwable the exception to examine
- * @param methodName the name of the method to find and invoke
- * @return the wrapped exception, or <code>null</code> if not found
- */
- // TODO: Remove in Lang 4.0
- private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) {
- Method method = null;
- try {
- method = throwable.getClass().getMethod(methodName);
- } catch (NoSuchMethodException ignored) { // NOPMD
- // exception ignored
- } catch (SecurityException ignored) { // NOPMD
- // exception ignored
- }
-
- if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) {
- try {
- return (Throwable) method.invoke(throwable);
- } catch (IllegalAccessException ignored) { // NOPMD
- // exception ignored
- } catch (IllegalArgumentException ignored) { // NOPMD
- // exception ignored
- } catch (InvocationTargetException ignored) { // NOPMD
- // exception ignored
- }
- }
- return null;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Counts the number of <code>Throwable</code> objects in the
- * exception chain.</p>
- *
- * <p>A throwable without cause will return <code>1</code>.
- * A throwable with one cause will return <code>2</code> and so on.
- * A <code>null</code> throwable will return <code>0</code>.</p>
- *
- * <p>From version 2.2, this method handles recursive cause structures
- * that might otherwise cause infinite loops. The cause chain is
- * processed until the end is reached, or until the next item in the
- * chain is already in the result set.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @return the count of throwables, zero if null input
- */
- public static int getThrowableCount(Throwable throwable) {
- return getThrowableList(throwable).size();
- }
-
- /**
- * <p>Returns the list of <code>Throwable</code> objects in the
- * exception chain.</p>
- *
- * <p>A throwable without cause will return an array containing
- * one element - the input throwable.
- * A throwable with one cause will return an array containing
- * two elements. - the input throwable and the cause throwable.
- * A <code>null</code> throwable will return an array of size zero.</p>
- *
- * <p>From version 2.2, this method handles recursive cause structures
- * that might otherwise cause infinite loops. The cause chain is
- * processed until the end is reached, or until the next item in the
- * chain is already in the result set.</p>
- *
- * @see #getThrowableList(Throwable)
- * @param throwable the throwable to inspect, may be null
- * @return the array of throwables, never null
- */
- public static Throwable[] getThrowables(Throwable throwable) {
- List<Throwable> list = getThrowableList(throwable);
- return list.toArray(new Throwable[list.size()]);
- }
-
- /**
- * <p>Returns the list of <code>Throwable</code> objects in the
- * exception chain.</p>
- *
- * <p>A throwable without cause will return a list containing
- * one element - the input throwable.
- * A throwable with one cause will return a list containing
- * two elements. - the input throwable and the cause throwable.
- * A <code>null</code> throwable will return a list of size zero.</p>
- *
- * <p>This method handles recursive cause structures that might
- * otherwise cause infinite loops. The cause chain is processed until
- * the end is reached, or until the next item in the chain is already
- * in the result set.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @return the list of throwables, never null
- * @since Commons Lang 2.2
- */
- public static List<Throwable> getThrowableList(Throwable throwable) {
- List<Throwable> list = new ArrayList<Throwable>();
- while (throwable != null && list.contains(throwable) == false) {
- list.add(throwable);
- throwable = ExceptionUtils.getCause(throwable);
- }
- return list;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns the (zero based) index of the first <code>Throwable</code>
- * that matches the specified class (exactly) in the exception chain.
- * Subclasses of the specified class do not match - see
- * {@link #indexOfType(Throwable, Class)} for the opposite.</p>
- *
- * <p>A <code>null</code> throwable returns <code>-1</code>.
- * A <code>null</code> type returns <code>-1</code>.
- * No match in the chain returns <code>-1</code>.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @param clazz the class to search for, subclasses do not match, null returns -1
- * @return the index into the throwable chain, -1 if no match or null input
- */
- public static int indexOfThrowable(Throwable throwable, Class<?> clazz) {
- return indexOf(throwable, clazz, 0, false);
- }
-
- /**
- * <p>Returns the (zero based) index of the first <code>Throwable</code>
- * that matches the specified type in the exception chain from
- * a specified index.
- * Subclasses of the specified class do not match - see
- * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p>
- *
- * <p>A <code>null</code> throwable returns <code>-1</code>.
- * A <code>null</code> type returns <code>-1</code>.
- * No match in the chain returns <code>-1</code>.
- * A negative start index is treated as zero.
- * A start index greater than the number of throwables returns <code>-1</code>.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @param clazz the class to search for, subclasses do not match, null returns -1
- * @param fromIndex the (zero based) index of the starting position,
- * negative treated as zero, larger than chain size returns -1
- * @return the index into the throwable chain, -1 if no match or null input
- */
- public static int indexOfThrowable(Throwable throwable, Class<?> clazz, int fromIndex) {
- return indexOf(throwable, clazz, fromIndex, false);
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns the (zero based) index of the first <code>Throwable</code>
- * that matches the specified class or subclass in the exception chain.
- * Subclasses of the specified class do match - see
- * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
- *
- * <p>A <code>null</code> throwable returns <code>-1</code>.
- * A <code>null</code> type returns <code>-1</code>.
- * No match in the chain returns <code>-1</code>.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @param type the type to search for, subclasses match, null returns -1
- * @return the index into the throwable chain, -1 if no match or null input
- * @since 2.1
- */
- public static int indexOfType(Throwable throwable, Class<?> type) {
- return indexOf(throwable, type, 0, true);
- }
-
- /**
- * <p>Returns the (zero based) index of the first <code>Throwable</code>
- * that matches the specified type in the exception chain from
- * a specified index.
- * Subclasses of the specified class do match - see
- * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p>
- *
- * <p>A <code>null</code> throwable returns <code>-1</code>.
- * A <code>null</code> type returns <code>-1</code>.
- * No match in the chain returns <code>-1</code>.
- * A negative start index is treated as zero.
- * A start index greater than the number of throwables returns <code>-1</code>.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @param type the type to search for, subclasses match, null returns -1
- * @param fromIndex the (zero based) index of the starting position,
- * negative treated as zero, larger than chain size returns -1
- * @return the index into the throwable chain, -1 if no match or null input
- * @since 2.1
- */
- public static int indexOfType(Throwable throwable, Class<?> type, int fromIndex) {
- return indexOf(throwable, type, fromIndex, true);
- }
-
- /**
- * <p>Worker method for the <code>indexOfType</code> methods.</p>
- *
- * @param throwable the throwable to inspect, may be null
- * @param type the type to search for, subclasses match, null returns -1
- * @param fromIndex the (zero based) index of the starting position,
- * negative treated as zero, larger than chain size returns -1
- * @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares
- * using references
- * @return index of the <code>type</code> within throwables nested withing the specified <code>throwable</code>
- */
- private static int indexOf(Throwable throwable, Class<?> type, int fromIndex, boolean subclass) {
- if (throwable == null || type == null) {
- return -1;
- }
- if (fromIndex < 0) {
- fromIndex = 0;
- }
- Throwable[] throwables = ExceptionUtils.getThrowables(throwable);
- if (fromIndex >= throwables.length) {
- return -1;
- }
- if (subclass) {
- for (int i = fromIndex; i < throwables.length; i++) {
- if (type.isAssignableFrom(throwables[i].getClass())) {
- return i;
- }
- }
- } else {
- for (int i = fromIndex; i < throwables.length; i++) {
- if (type.equals(throwables[i].getClass())) {
- return i;
- }
- }
- }
- return -1;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Prints a compact stack trace for the root cause of a throwable
- * to <code>System.err</code>.</p>
- *
- * <p>The compact stack trace starts with the root cause and prints
- * stack frames up to the place where it was caught and wrapped.
- * Then it prints the wrapped exception and continues with stack frames
- * until the wrapper exception is caught and wrapped again, etc.</p>
- *
- * <p>The output of this method is consistent across JDK versions.
- * Note that this is the opposite order to the JDK1.4 display.</p>
- *
- * <p>The method is equivalent to <code>printStackTrace</code> for throwables
- * that don't have nested causes.</p>
- *
- * @param throwable the throwable to output
- * @since 2.0
- */
- public static void printRootCauseStackTrace(Throwable throwable) {
- printRootCauseStackTrace(throwable, System.err);
- }
-
- /**
- * <p>Prints a compact stack trace for the root cause of a throwable.</p>
- *
- * <p>The compact stack trace starts with the root cause and prints
- * stack frames up to the place where it was caught and wrapped.
- * Then it prints the wrapped exception and continues with stack frames
- * until the wrapper exception is caught and wrapped again, etc.</p>
- *
- * <p>The output of this method is consistent across JDK versions.
- * Note that this is the opposite order to the JDK1.4 display.</p>
- *
- * <p>The method is equivalent to <code>printStackTrace</code> for throwables
- * that don't have nested causes.</p>
- *
- * @param throwable the throwable to output, may be null
- * @param stream the stream to output to, may not be null
- * @throws IllegalArgumentException if the stream is <code>null</code>
- * @since 2.0
- */
- public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) {
- if (throwable == null) {
- return;
- }
- if (stream == null) {
- throw new IllegalArgumentException("The PrintStream must not be null");
- }
- String trace[] = getRootCauseStackTrace(throwable);
- for (String element : trace) {
- stream.println(element);
- }
- stream.flush();
- }
-
- /**
- * <p>Prints a compact stack trace for the root cause of a throwable.</p>
- *
- * <p>The compact stack trace starts with the root cause and prints
- * stack frames up to the place where it was caught and wrapped.
- * Then it prints the wrapped exception and continues with stack frames
- * until the wrapper exception is caught and wrapped again, etc.</p>
- *
- * <p>The output of this method is consistent across JDK versions.
- * Note that this is the opposite order to the JDK1.4 display.</p>
- *
- * <p>The method is equivalent to <code>printStackTrace</code> for throwables
- * that don't have nested causes.</p>
- *
- * @param throwable the throwable to output, may be null
- * @param writer the writer to output to, may not be null
- * @throws IllegalArgumentException if the writer is <code>null</code>
- * @since 2.0
- */
- public static void printRootCauseStackTrace(Throwable throwable, PrintWriter writer) {
- if (throwable == null) {
- return;
- }
- if (writer == null) {
- throw new IllegalArgumentException("The PrintWriter must not be null");
- }
- String trace[] = getRootCauseStackTrace(throwable);
- for (String element : trace) {
- writer.println(element);
- }
- writer.flush();
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Creates a compact stack trace for the root cause of the supplied
- * <code>Throwable</code>.</p>
- *
- * <p>The output of this method is consistent across JDK versions.
- * It consists of the root exception followed by each of its wrapping
- * exceptions separated by '[wrapped]'. Note that this is the opposite
- * order to the JDK1.4 display.</p>
- *
- * @param throwable the throwable to examine, may be null
- * @return an array of stack trace frames, never null
- * @since 2.0
- */
- public static String[] getRootCauseStackTrace(Throwable throwable) {
- if (throwable == null) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- Throwable throwables[] = getThrowables(throwable);
- int count = throwables.length;
- List<String> frames = new ArrayList<String>();
- List<String> nextTrace = getStackFrameList(throwables[count - 1]);
- for (int i = count; --i >= 0;) {
- List<String> trace = nextTrace;
- if (i != 0) {
- nextTrace = getStackFrameList(throwables[i - 1]);
- removeCommonFrames(trace, nextTrace);
- }
- if (i == count - 1) {
- frames.add(throwables[i].toString());
- } else {
- frames.add(WRAPPED_MARKER + throwables[i].toString());
- }
- for (int j = 0; j < trace.size(); j++) {
- frames.add(trace.get(j));
- }
- }
- return frames.toArray(new String[frames.size()]);
- }
-
- /**
- * <p>Removes common frames from the cause trace given the two stack traces.</p>
- *
- * @param causeFrames stack trace of a cause throwable
- * @param wrapperFrames stack trace of a wrapper throwable
- * @throws IllegalArgumentException if either argument is null
- * @since 2.0
- */
- public static void removeCommonFrames(List<String> causeFrames, List<String> wrapperFrames) {
- if (causeFrames == null || wrapperFrames == null) {
- throw new IllegalArgumentException("The List must not be null");
- }
- int causeFrameIndex = causeFrames.size() - 1;
- int wrapperFrameIndex = wrapperFrames.size() - 1;
- while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) {
- // Remove the frame from the cause trace if it is the same
- // as in the wrapper trace
- String causeFrame = causeFrames.get(causeFrameIndex);
- String wrapperFrame = wrapperFrames.get(wrapperFrameIndex);
- if (causeFrame.equals(wrapperFrame)) {
- causeFrames.remove(causeFrameIndex);
- }
- causeFrameIndex--;
- wrapperFrameIndex--;
- }
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the stack trace from a Throwable as a String.</p>
- *
- * <p>The result of this method vary by JDK version as this method
- * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
- * On JDK1.3 and earlier, the cause exception will not be shown
- * unless the specified throwable alters printStackTrace.</p>
- *
- * @param throwable the <code>Throwable</code> to be examined
- * @return the stack trace as generated by the exception's
- * <code>printStackTrace(PrintWriter)</code> method
- */
- public static String getStackTrace(Throwable throwable) {
- StringWriter sw = new StringWriter();
- PrintWriter pw = new PrintWriter(sw, true);
- throwable.printStackTrace(pw);
- return sw.getBuffer().toString();
- }
-
- /**
- * <p>Captures the stack trace associated with the specified
- * <code>Throwable</code> object, decomposing it into a list of
- * stack frames.</p>
- *
- * <p>The result of this method vary by JDK version as this method
- * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}.
- * On JDK1.3 and earlier, the cause exception will not be shown
- * unless the specified throwable alters printStackTrace.</p>
- *
- * @param throwable the <code>Throwable</code> to examine, may be null
- * @return an array of strings describing each stack frame, never null
- */
- public static String[] getStackFrames(Throwable throwable) {
- if (throwable == null) {
- return ArrayUtils.EMPTY_STRING_ARRAY;
- }
- return getStackFrames(getStackTrace(throwable));
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Returns an array where each element is a line from the argument.</p>
- *
- * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p>
- *
- * @param stackTrace a stack trace String
- * @return an array where each element is a line from the argument
- */
- static String[] getStackFrames(String stackTrace) {
- String linebreak = SystemUtils.LINE_SEPARATOR;
- StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
- List<String> list = new ArrayList<String>();
- while (frames.hasMoreTokens()) {
- list.add(frames.nextToken());
- }
- return list.toArray(new String[list.size()]);
- }
-
- /**
- * <p>Produces a <code>List</code> of stack frames - the message
- * is not included. Only the trace of the specified exception is
- * returned, any caused by trace is stripped.</p>
- *
- * <p>This works in most cases - it will only fail if the exception
- * message contains a line that starts with:
- * <code>" at".</code></p>
- *
- * @param t is any throwable
- * @return List of stack frames
- */
- static List<String> getStackFrameList(Throwable t) {
- String stackTrace = getStackTrace(t);
- String linebreak = SystemUtils.LINE_SEPARATOR;
- StringTokenizer frames = new StringTokenizer(stackTrace, linebreak);
- List<String> list = new ArrayList<String>();
- boolean traceStarted = false;
- while (frames.hasMoreTokens()) {
- String token = frames.nextToken();
- // Determine if the line starts with <whitespace>at
- int at = token.indexOf("at");
- if (at != -1 && token.substring(0, at).trim().length() == 0) {
- traceStarted = true;
- list.add(token);
- } else if (traceStarted) {
- break;
- }
- }
- return list;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets a short message summarising the exception.
- * <p>
- * The message returned is of the form
- * {ClassNameWithoutPackage}: {ThrowableMessage}
- *
- * @param th the throwable to get a message for, null returns empty string
- * @return the message, non-null
- * @since Commons Lang 2.2
- */
- public static String getMessage(Throwable th) {
- if (th == null) {
- return "";
- }
- String clsName = ClassUtils.getShortClassName(th, null);
- String msg = th.getMessage();
- return clsName + ": " + StringUtils.defaultString(msg);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets a short message summarising the root cause exception.
- * <p>
- * The message returned is of the form
- * {ClassNameWithoutPackage}: {ThrowableMessage}
- *
- * @param th the throwable to get a message for, null returns empty string
- * @return the message, non-null
- * @since Commons Lang 2.2
- */
- public static String getRootCauseMessage(Throwable th) {
- Throwable root = ExceptionUtils.getRootCause(th);
- root = (root == null ? th : root);
- return getMessage(root);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + * <p>Provides utilities for manipulating and examining + * <code>Throwable</code> objects.</p> + * + * @since 1.0 + * @version $Id: ExceptionUtils.java 1144929 2011-07-10 18:26:16Z ggregory $ + */ +public class ExceptionUtils { + + /** + * <p>Used when printing stack frames to denote the start of a + * wrapped exception.</p> + * + * <p>Package private for accessibility by test suite.</p> + */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + * <p>The names of methods commonly used to access a wrapped exception.</p> + */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + * <p> + * Public constructor allows an instance of <code>ExceptionUtils</code> to be created, although that is not + * normally necessary. + * </p> + */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + * <p>Returns the default names used when searching for the cause of an exception.</p> + * + * <p>This may be modified and used in the overloaded getCause(Throwable, String[]) method.</p> + * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> + * + * <p>The method searches for methods with specific names that return a + * <code>Throwable</code> object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + * <p>The default list searched for are:</p> + * <ul> + * <li><code>getCause()</code></li> + * <li><code>getNextException()</code></li> + * <li><code>getTargetException()</code></li> + * <li><code>getException()</code></li> + * <li><code>getSourceException()</code></li> + * <li><code>getRootCause()</code></li> + * <li><code>getCausedByException()</code></li> + * <li><code>getNested()</code></li> + * </ul> + * + * <p>If none of the above is found, returns <code>null</code>.</p> + * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the <code>Throwable</code>, + * <code>null</code> if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static Throwable getCause(Throwable throwable) { + return getCause(throwable, CAUSE_METHOD_NAMES); + } + + /** + * <p>Introspects the <code>Throwable</code> to obtain the cause.</p> + * + * <p>A <code>null</code> set of method names means use the default set. + * A <code>null</code> in the set of method names will be ignored.</p> + * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the <code>Throwable</code>, + * <code>null</code> if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static Throwable getCause(Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + methodNames = CAUSE_METHOD_NAMES; + } + + for (String methodName : methodNames) { + if (methodName != null) { + Throwable cause = getCauseUsingMethodName(throwable, methodName); + if (cause != null) { + return cause; + } + } + } + + return null; + } + + /** + * <p>Introspects the <code>Throwable</code> to obtain the root cause.</p> + * + * <p>This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.</p> + * + * <p>From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.</p> + * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the <code>Throwable</code>, + * <code>null</code> if none found or null throwable input + */ + public static Throwable getRootCause(Throwable throwable) { + List<Throwable> list = getThrowableList(throwable); + return (list.size() < 2 ? null : (Throwable)list.get(list.size() - 1)); + } + + /** + * <p>Finds a <code>Throwable</code> by method name.</p> + * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or <code>null</code> if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + * <p>Counts the number of <code>Throwable</code> objects in the + * exception chain.</p> + * + * <p>A throwable without cause will return <code>1</code>. + * A throwable with one cause will return <code>2</code> and so on. + * A <code>null</code> throwable will return <code>0</code>.</p> + * + * <p>From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.</p> + * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + * <p>Returns the list of <code>Throwable</code> objects in the + * exception chain.</p> + * + * <p>A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A <code>null</code> throwable will return an array of size zero.</p> + * + * <p>From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.</p> + * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(Throwable throwable) { + List<Throwable> list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + * <p>Returns the list of <code>Throwable</code> objects in the + * exception chain.</p> + * + * <p>A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A <code>null</code> throwable will return a list of size zero.</p> + * + * <p>This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.</p> + * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List<Throwable> getThrowableList(Throwable throwable) { + List<Throwable> list = new ArrayList<Throwable>(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + * <p>Returns the (zero based) index of the first <code>Throwable</code> + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.</p> + * + * <p>A <code>null</code> throwable returns <code>-1</code>. + * A <code>null</code> type returns <code>-1</code>. + * No match in the chain returns <code>-1</code>.</p> + * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(Throwable throwable, Class<?> clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + * <p>Returns the (zero based) index of the first <code>Throwable</code> + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.</p> + * + * <p>A <code>null</code> throwable returns <code>-1</code>. + * A <code>null</code> type returns <code>-1</code>. + * No match in the chain returns <code>-1</code>. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns <code>-1</code>.</p> + * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(Throwable throwable, Class<?> clazz, int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + * <p>Returns the (zero based) index of the first <code>Throwable</code> + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> + * + * <p>A <code>null</code> throwable returns <code>-1</code>. + * A <code>null</code> type returns <code>-1</code>. + * No match in the chain returns <code>-1</code>.</p> + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class<?> type) { + return indexOf(throwable, type, 0, true); + } + + /** + * <p>Returns the (zero based) index of the first <code>Throwable</code> + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.</p> + * + * <p>A <code>null</code> throwable returns <code>-1</code>. + * A <code>null</code> type returns <code>-1</code>. + * No match in the chain returns <code>-1</code>. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns <code>-1</code>.</p> + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class<?> type, int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + * <p>Worker method for the <code>indexOfType</code> methods.</p> + * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if <code>true</code>, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the <code>type</code> within throwables nested withing the specified <code>throwable</code> + */ + private static int indexOf(Throwable throwable, Class<?> type, int fromIndex, boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + * <p>Prints a compact stack trace for the root cause of a throwable + * to <code>System.err</code>.</p> + * + * <p>The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.</p> + * + * <p>The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.</p> + * + * <p>The method is equivalent to <code>printStackTrace</code> for throwables + * that don't have nested causes.</p> + * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + * <p>Prints a compact stack trace for the root cause of a throwable.</p> + * + * <p>The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.</p> + * + * <p>The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.</p> + * + * <p>The method is equivalent to <code>printStackTrace</code> for throwables + * that don't have nested causes.</p> + * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is <code>null</code> + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + String trace[] = getRootCauseStackTrace(throwable); + for (String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + * <p>Prints a compact stack trace for the root cause of a throwable.</p> + * + * <p>The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.</p> + * + * <p>The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.</p> + * + * <p>The method is equivalent to <code>printStackTrace</code> for throwables + * that don't have nested causes.</p> + * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is <code>null</code> + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable, PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + String trace[] = getRootCauseStackTrace(throwable); + for (String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + * <p>Creates a compact stack trace for the root cause of the supplied + * <code>Throwable</code>.</p> + * + * <p>The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.</p> + * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + Throwable throwables[] = getThrowables(throwable); + int count = throwables.length; + List<String> frames = new ArrayList<String>(); + List<String> nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + List<String> trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + * <p>Removes common frames from the cause trace given the two stack traces.</p> + * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(List<String> causeFrames, List<String> wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + String causeFrame = causeFrames.get(causeFrameIndex); + String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + * <p>Gets the stack trace from a Throwable as a String.</p> + * + * <p>The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.</p> + * + * @param throwable the <code>Throwable</code> to be examined + * @return the stack trace as generated by the exception's + * <code>printStackTrace(PrintWriter)</code> method + */ + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + * <p>Captures the stack trace associated with the specified + * <code>Throwable</code> object, decomposing it into a list of + * stack frames.</p> + * + * <p>The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.</p> + * + * @param throwable the <code>Throwable</code> to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + * <p>Returns an array where each element is a line from the argument.</p> + * + * <p>The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.</p> + * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(String stackTrace) { + String linebreak = SystemUtils.LINE_SEPARATOR; + StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + List<String> list = new ArrayList<String>(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + * <p>Produces a <code>List</code> of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.</p> + * + * <p>This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * <code>" at".</code></p> + * + * @param t is any throwable + * @return List of stack frames + */ + static List<String> getStackFrameList(Throwable t) { + String stackTrace = getStackTrace(t); + String linebreak = SystemUtils.LINE_SEPARATOR; + StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + List<String> list = new ArrayList<String>(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + String token = frames.nextToken(); + // Determine if the line starts with <whitespace>at + int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().length() == 0) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + * <p> + * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(Throwable th) { + if (th == null) { + return ""; + } + String clsName = ClassUtils.getShortClassName(th, null); + String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + * <p> + * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = (root == null ? th : root); + return getMessage(root); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/Mutable.java b/src/org/apache/commons/lang3/mutable/Mutable.java index 64514f0..25a78f2 100644 --- a/src/org/apache/commons/lang3/mutable/Mutable.java +++ b/src/org/apache/commons/lang3/mutable/Mutable.java @@ -1,54 +1,54 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.lang3.mutable;
-
-/**
- * Provides mutable access to a value.
- * <p>
- * <code>Mutable</code> is used as a generic interface to the implementations in this package.
- * <p>
- * A typical use case would be to enable a primitive or string to be passed to a method and allow that method to
- * effectively change the value of the primitive/string. Another use case is to store a frequently changing primitive in
- * a collection (for example a total in a map) without needing to create new Integer/Long wrapper objects.
- *
- * @since 2.1
- * @param <T> the type to set and get
- * @version $Id: Mutable.java 1153213 2011-08-02 17:35:39Z ggregory $
- */
-public interface Mutable<T> {
-
- /**
- * Gets the value of this mutable.
- *
- * @return the stored value
- */
- T getValue();
-
- /**
- * Sets the value of this mutable.
- *
- * @param value
- * the value to store
- * @throws NullPointerException
- * if the object is null and null is invalid
- * @throws ClassCastException
- * if the type is invalid
- */
- void setValue(T value);
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +/** + * Provides mutable access to a value. + * <p> + * <code>Mutable</code> is used as a generic interface to the implementations in this package. + * <p> + * A typical use case would be to enable a primitive or string to be passed to a method and allow that method to + * effectively change the value of the primitive/string. Another use case is to store a frequently changing primitive in + * a collection (for example a total in a map) without needing to create new Integer/Long wrapper objects. + * + * @since 2.1 + * @param <T> the type to set and get + * @version $Id: Mutable.java 1153213 2011-08-02 17:35:39Z ggregory $ + */ +public interface Mutable<T> { + + /** + * Gets the value of this mutable. + * + * @return the stored value + */ + T getValue(); + + /** + * Sets the value of this mutable. + * + * @param value + * the value to store + * @throws NullPointerException + * if the object is null and null is invalid + * @throws ClassCastException + * if the type is invalid + */ + void setValue(T value); + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableBoolean.java b/src/org/apache/commons/lang3/mutable/MutableBoolean.java index f2e0a48..6a12b61 100644 --- a/src/org/apache/commons/lang3/mutable/MutableBoolean.java +++ b/src/org/apache/commons/lang3/mutable/MutableBoolean.java @@ -1,193 +1,193 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.lang3.mutable;
-
-import java.io.Serializable;
-
-/**
- * A mutable <code>boolean</code> wrapper.
- * <p>
- * Note that as MutableBoolean does not extend Boolean, it is not treated by String.format as a Boolean parameter.
- *
- * @see Boolean
- * @since 2.2
- * @version $Id: MutableBoolean.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparable<MutableBoolean> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = -4830728138360036487L;
-
- /** The mutable value. */
- private boolean value;
-
- /**
- * Constructs a new MutableBoolean with the default value of false.
- */
- public MutableBoolean() {
- super();
- }
-
- /**
- * Constructs a new MutableBoolean with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableBoolean(boolean value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableBoolean with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableBoolean(Boolean value) {
- super();
- this.value = value.booleanValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Boolean instance.
- *
- * @return the value as a Boolean, never null
- */
- public Boolean getValue() {
- return Boolean.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(boolean value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Boolean instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Boolean value) {
- this.value = value.booleanValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Checks if the current value is <code>true</code>.
- *
- * @return <code>true</code> if the current value is <code>true</code>
- * @since 2.5
- */
- public boolean isTrue() {
- return value == true;
- }
-
- /**
- * Checks if the current value is <code>false</code>.
- *
- * @return <code>true</code> if the current value is <code>false</code>
- * @since 2.5
- */
- public boolean isFalse() {
- return value == false;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the value of this MutableBoolean as a boolean.
- *
- * @return the boolean value represented by this object.
- */
- public boolean booleanValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Boolean.
- *
- * @return a Boolean instance containing the value from this mutable, never null
- * @since 2.5
- */
- public Boolean toBoolean() {
- return Boolean.valueOf(booleanValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is
- * not <code>null</code> and is an <code>MutableBoolean</code> object that contains the same
- * <code>boolean</code> value as this object.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MutableBoolean) {
- return value == ((MutableBoolean) obj).booleanValue();
- }
- return false;
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return the hash code returned by <code>Boolean.TRUE</code> or <code>Boolean.FALSE</code>
- */
- @Override
- public int hashCode() {
- return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- * where false is less than true
- */
- public int compareTo(MutableBoolean other) {
- boolean anotherVal = other.value;
- return value == anotherVal ? 0 : (value ? 1 : -1);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +import java.io.Serializable; + +/** + * A mutable <code>boolean</code> wrapper. + * <p> + * Note that as MutableBoolean does not extend Boolean, it is not treated by String.format as a Boolean parameter. + * + * @see Boolean + * @since 2.2 + * @version $Id: MutableBoolean.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableBoolean implements Mutable<Boolean>, Serializable, Comparable<MutableBoolean> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -4830728138360036487L; + + /** The mutable value. */ + private boolean value; + + /** + * Constructs a new MutableBoolean with the default value of false. + */ + public MutableBoolean() { + super(); + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store + */ + public MutableBoolean(boolean value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableBoolean(Boolean value) { + super(); + this.value = value.booleanValue(); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Boolean instance. + * + * @return the value as a Boolean, never null + */ + public Boolean getValue() { + return Boolean.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(boolean value) { + this.value = value; + } + + /** + * Sets the value from any Boolean instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Boolean value) { + this.value = value.booleanValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks if the current value is <code>true</code>. + * + * @return <code>true</code> if the current value is <code>true</code> + * @since 2.5 + */ + public boolean isTrue() { + return value == true; + } + + /** + * Checks if the current value is <code>false</code>. + * + * @return <code>true</code> if the current value is <code>false</code> + * @since 2.5 + */ + public boolean isFalse() { + return value == false; + } + + //----------------------------------------------------------------------- + /** + * Returns the value of this MutableBoolean as a boolean. + * + * @return the boolean value represented by this object. + */ + public boolean booleanValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Boolean. + * + * @return a Boolean instance containing the value from this mutable, never null + * @since 2.5 + */ + public Boolean toBoolean() { + return Boolean.valueOf(booleanValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is + * not <code>null</code> and is an <code>MutableBoolean</code> object that contains the same + * <code>boolean</code> value as this object. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableBoolean) { + return value == ((MutableBoolean) obj).booleanValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return the hash code returned by <code>Boolean.TRUE</code> or <code>Boolean.FALSE</code> + */ + @Override + public int hashCode() { + return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * where false is less than true + */ + public int compareTo(MutableBoolean other) { + boolean anotherVal = other.value; + return value == anotherVal ? 0 : (value ? 1 : -1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableByte.java b/src/org/apache/commons/lang3/mutable/MutableByte.java index 129d86d..0a50108 100644 --- a/src/org/apache/commons/lang3/mutable/MutableByte.java +++ b/src/org/apache/commons/lang3/mutable/MutableByte.java @@ -1,283 +1,283 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>byte</code> wrapper.
- * <p>
- * Note that as MutableByte does not extend Byte, it is not treated by String.format as a Byte parameter.
- *
- * @see Byte
- * @since 2.1
- * @version $Id: MutableByte.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableByte extends Number implements Comparable<MutableByte>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = -1585823265L;
-
- /** The mutable value. */
- private byte value;
-
- /**
- * Constructs a new MutableByte with the default value of zero.
- */
- public MutableByte() {
- super();
- }
-
- /**
- * Constructs a new MutableByte with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableByte(byte value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableByte with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableByte(Number value) {
- super();
- this.value = value.byteValue();
- }
-
- /**
- * Constructs a new MutableByte parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into a byte
- * @since 2.5
- */
- public MutableByte(String value) throws NumberFormatException {
- super();
- this.value = Byte.parseByte(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Byte instance.
- *
- * @return the value as a Byte, never null
- */
- public Byte getValue() {
- return Byte.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(byte value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.byteValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @since Commons Lang 2.2
- */
- public void add(byte operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.byteValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @since Commons Lang 2.2
- */
- public void subtract(byte operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.byteValue();
- }
-
- //-----------------------------------------------------------------------
- // shortValue relies on Number implementation
- /**
- * Returns the value of this MutableByte as a byte.
- *
- * @return the numeric value represented by this object after conversion to type byte.
- */
- @Override
- public byte byteValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableByte as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableByte as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableByte as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableByte as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Byte.
- *
- * @return a Byte instance containing the value from this mutable
- */
- public Byte toByte() {
- return Byte.valueOf(byteValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is
- * not <code>null</code> and is a <code>MutableByte</code> object that contains the same <code>byte</code> value
- * as this object.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MutableByte) {
- return value == ((MutableByte) obj).byteValue();
- }
- return false;
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableByte other) {
- byte anotherVal = other.value;
- return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>byte</code> wrapper. + * <p> + * Note that as MutableByte does not extend Byte, it is not treated by String.format as a Byte parameter. + * + * @see Byte + * @since 2.1 + * @version $Id: MutableByte.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableByte extends Number implements Comparable<MutableByte>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -1585823265L; + + /** The mutable value. */ + private byte value; + + /** + * Constructs a new MutableByte with the default value of zero. + */ + public MutableByte() { + super(); + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store + */ + public MutableByte(byte value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableByte(Number value) { + super(); + this.value = value.byteValue(); + } + + /** + * Constructs a new MutableByte parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a byte + * @since 2.5 + */ + public MutableByte(String value) throws NumberFormatException { + super(); + this.value = Byte.parseByte(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Byte instance. + * + * @return the value as a Byte, never null + */ + public Byte getValue() { + return Byte.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(byte value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.byteValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(byte operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.byteValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(byte operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.byteValue(); + } + + //----------------------------------------------------------------------- + // shortValue relies on Number implementation + /** + * Returns the value of this MutableByte as a byte. + * + * @return the numeric value represented by this object after conversion to type byte. + */ + @Override + public byte byteValue() { + return value; + } + + /** + * Returns the value of this MutableByte as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Byte. + * + * @return a Byte instance containing the value from this mutable + */ + public Byte toByte() { + return Byte.valueOf(byteValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is + * not <code>null</code> and is a <code>MutableByte</code> object that contains the same <code>byte</code> value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableByte) { + return value == ((MutableByte) obj).byteValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableByte other) { + byte anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableDouble.java b/src/org/apache/commons/lang3/mutable/MutableDouble.java index f4acfa9..588a711 100644 --- a/src/org/apache/commons/lang3/mutable/MutableDouble.java +++ b/src/org/apache/commons/lang3/mutable/MutableDouble.java @@ -1,312 +1,312 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>double</code> wrapper.
- * <p>
- * Note that as MutableDouble does not extend Double, it is not treated by String.format as a Double parameter.
- *
- * @see Double
- * @since 2.1
- * @version $Id: MutableDouble.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableDouble extends Number implements Comparable<MutableDouble>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 1587163916L;
-
- /** The mutable value. */
- private double value;
-
- /**
- * Constructs a new MutableDouble with the default value of zero.
- */
- public MutableDouble() {
- super();
- }
-
- /**
- * Constructs a new MutableDouble with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableDouble(double value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableDouble with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableDouble(Number value) {
- super();
- this.value = value.doubleValue();
- }
-
- /**
- * Constructs a new MutableDouble parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into a double
- * @since 2.5
- */
- public MutableDouble(String value) throws NumberFormatException {
- super();
- this.value = Double.parseDouble(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Double instance.
- *
- * @return the value as a Double, never null
- */
- public Double getValue() {
- return Double.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(double value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.doubleValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Checks whether the double value is the special NaN value.
- *
- * @return true if NaN
- */
- public boolean isNaN() {
- return Double.isNaN(value);
- }
-
- /**
- * Checks whether the double value is infinite.
- *
- * @return true if infinite
- */
- public boolean isInfinite() {
- return Double.isInfinite(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add
- * @since Commons Lang 2.2
- */
- public void add(double operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.doubleValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @since Commons Lang 2.2
- */
- public void subtract(double operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.doubleValue();
- }
-
- //-----------------------------------------------------------------------
- // shortValue and byteValue rely on Number implementation
- /**
- * Returns the value of this MutableDouble as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return (int) value;
- }
-
- /**
- * Returns the value of this MutableDouble as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return (long) value;
- }
-
- /**
- * Returns the value of this MutableDouble as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return (float) value;
- }
-
- /**
- * Returns the value of this MutableDouble as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Double.
- *
- * @return a Double instance containing the value from this mutable, never null
- */
- public Double toDouble() {
- return Double.valueOf(doubleValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object against the specified object. The result is <code>true</code> if and only if the argument
- * is not <code>null</code> and is a <code>Double</code> object that represents a double that has the identical
- * bit pattern to the bit pattern of the double represented by this object. For this purpose, two
- * <code>double</code> values are considered to be the same if and only if the method
- * {@link Double#doubleToLongBits(double)}returns the same long value when applied to each.
- * <p>
- * Note that in most cases, for two instances of class <code>Double</code>,<code>d1</code> and <code>d2</code>,
- * the value of <code>d1.equals(d2)</code> is <code>true</code> if and only if <blockquote>
- *
- * <pre>
- * d1.doubleValue() == d2.doubleValue()
- * </pre>
- *
- * </blockquote>
- * <p>
- * also has the value <code>true</code>. However, there are two exceptions:
- * <ul>
- * <li>If <code>d1</code> and <code>d2</code> both represent <code>Double.NaN</code>, then the
- * <code>equals</code> method returns <code>true</code>, even though <code>Double.NaN==Double.NaN</code> has
- * the value <code>false</code>.
- * <li>If <code>d1</code> represents <code>+0.0</code> while <code>d2</code> represents <code>-0.0</code>,
- * or vice versa, the <code>equal</code> test has the value <code>false</code>, even though
- * <code>+0.0==-0.0</code> has the value <code>true</code>. This allows hashtables to operate properly.
- * </ul>
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof MutableDouble)
- && (Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value));
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- long bits = Double.doubleToLongBits(value);
- return (int) (bits ^ (bits >>> 32));
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableDouble other) {
- double anotherVal = other.value;
- return Double.compare(value, anotherVal);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>double</code> wrapper. + * <p> + * Note that as MutableDouble does not extend Double, it is not treated by String.format as a Double parameter. + * + * @see Double + * @since 2.1 + * @version $Id: MutableDouble.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableDouble extends Number implements Comparable<MutableDouble>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1587163916L; + + /** The mutable value. */ + private double value; + + /** + * Constructs a new MutableDouble with the default value of zero. + */ + public MutableDouble() { + super(); + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store + */ + public MutableDouble(double value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableDouble(Number value) { + super(); + this.value = value.doubleValue(); + } + + /** + * Constructs a new MutableDouble parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a double + * @since 2.5 + */ + public MutableDouble(String value) throws NumberFormatException { + super(); + this.value = Double.parseDouble(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Double instance. + * + * @return the value as a Double, never null + */ + public Double getValue() { + return Double.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(double value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.doubleValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the double value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Double.isNaN(value); + } + + /** + * Checks whether the double value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Double.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add + * @since Commons Lang 2.2 + */ + public void add(double operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.doubleValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(double operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.doubleValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableDouble as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableDouble as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableDouble as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return (float) value; + } + + /** + * Returns the value of this MutableDouble as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Double. + * + * @return a Double instance containing the value from this mutable, never null + */ + public Double toDouble() { + return Double.valueOf(doubleValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against the specified object. The result is <code>true</code> if and only if the argument + * is not <code>null</code> and is a <code>Double</code> object that represents a double that has the identical + * bit pattern to the bit pattern of the double represented by this object. For this purpose, two + * <code>double</code> values are considered to be the same if and only if the method + * {@link Double#doubleToLongBits(double)}returns the same long value when applied to each. + * <p> + * Note that in most cases, for two instances of class <code>Double</code>,<code>d1</code> and <code>d2</code>, + * the value of <code>d1.equals(d2)</code> is <code>true</code> if and only if <blockquote> + * + * <pre> + * d1.doubleValue() == d2.doubleValue() + * </pre> + * + * </blockquote> + * <p> + * also has the value <code>true</code>. However, there are two exceptions: + * <ul> + * <li>If <code>d1</code> and <code>d2</code> both represent <code>Double.NaN</code>, then the + * <code>equals</code> method returns <code>true</code>, even though <code>Double.NaN==Double.NaN</code> has + * the value <code>false</code>. + * <li>If <code>d1</code> represents <code>+0.0</code> while <code>d2</code> represents <code>-0.0</code>, + * or vice versa, the <code>equal</code> test has the value <code>false</code>, even though + * <code>+0.0==-0.0</code> has the value <code>true</code>. This allows hashtables to operate properly. + * </ul> + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof MutableDouble) + && (Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value)); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + long bits = Double.doubleToLongBits(value); + return (int) (bits ^ (bits >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableDouble other) { + double anotherVal = other.value; + return Double.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableFloat.java b/src/org/apache/commons/lang3/mutable/MutableFloat.java index b7e0cd0..35f233c 100644 --- a/src/org/apache/commons/lang3/mutable/MutableFloat.java +++ b/src/org/apache/commons/lang3/mutable/MutableFloat.java @@ -1,313 +1,313 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>float</code> wrapper.
- * <p>
- * Note that as MutableFloat does not extend Float, it is not treated by String.format as a Float parameter.
- *
- * @see Float
- * @since 2.1
- * @version $Id: MutableFloat.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableFloat extends Number implements Comparable<MutableFloat>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 5787169186L;
-
- /** The mutable value. */
- private float value;
-
- /**
- * Constructs a new MutableFloat with the default value of zero.
- */
- public MutableFloat() {
- super();
- }
-
- /**
- * Constructs a new MutableFloat with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableFloat(float value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableFloat with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableFloat(Number value) {
- super();
- this.value = value.floatValue();
- }
-
- /**
- * Constructs a new MutableFloat parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into a float
- * @since 2.5
- */
- public MutableFloat(String value) throws NumberFormatException {
- super();
- this.value = Float.parseFloat(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Float instance.
- *
- * @return the value as a Float, never null
- */
- public Float getValue() {
- return Float.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(float value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.floatValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Checks whether the float value is the special NaN value.
- *
- * @return true if NaN
- */
- public boolean isNaN() {
- return Float.isNaN(value);
- }
-
- /**
- * Checks whether the float value is infinite.
- *
- * @return true if infinite
- */
- public boolean isInfinite() {
- return Float.isInfinite(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @since Commons Lang 2.2
- */
- public void add(float operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.floatValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract
- * @since Commons Lang 2.2
- */
- public void subtract(float operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.floatValue();
- }
-
- //-----------------------------------------------------------------------
- // shortValue and byteValue rely on Number implementation
- /**
- * Returns the value of this MutableFloat as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return (int) value;
- }
-
- /**
- * Returns the value of this MutableFloat as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return (long) value;
- }
-
- /**
- * Returns the value of this MutableFloat as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableFloat as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Float.
- *
- * @return a Float instance containing the value from this mutable, never null
- */
- public Float toFloat() {
- return Float.valueOf(floatValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object against some other object. The result is <code>true</code> if and only if the argument is
- * not <code>null</code> and is a <code>Float</code> object that represents a <code>float</code> that has the
- * identical bit pattern to the bit pattern of the <code>float</code> represented by this object. For this
- * purpose, two float values are considered to be the same if and only if the method
- * {@link Float#floatToIntBits(float)}returns the same int value when applied to each.
- * <p>
- * Note that in most cases, for two instances of class <code>Float</code>,<code>f1</code> and <code>f2</code>,
- * the value of <code>f1.equals(f2)</code> is <code>true</code> if and only if <blockquote>
- *
- * <pre>
- * f1.floatValue() == f2.floatValue()
- * </pre>
- *
- * </blockquote>
- * <p>
- * also has the value <code>true</code>. However, there are two exceptions:
- * <ul>
- * <li>If <code>f1</code> and <code>f2</code> both represent <code>Float.NaN</code>, then the
- * <code>equals</code> method returns <code>true</code>, even though <code>Float.NaN==Float.NaN</code> has
- * the value <code>false</code>.
- * <li>If <code>f1</code> represents <code>+0.0f</code> while <code>f2</code> represents <code>-0.0f</code>,
- * or vice versa, the <code>equal</code> test has the value <code>false</code>, even though
- * <code>0.0f==-0.0f</code> has the value <code>true</code>.
- * </ul>
- * This definition allows hashtables to operate properly.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- * @see java.lang.Float#floatToIntBits(float)
- */
- @Override
- public boolean equals(Object obj) {
- return (obj instanceof MutableFloat)
- && (Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value));
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- return Float.floatToIntBits(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableFloat other) {
- float anotherVal = other.value;
- return Float.compare(value, anotherVal);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>float</code> wrapper. + * <p> + * Note that as MutableFloat does not extend Float, it is not treated by String.format as a Float parameter. + * + * @see Float + * @since 2.1 + * @version $Id: MutableFloat.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableFloat extends Number implements Comparable<MutableFloat>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 5787169186L; + + /** The mutable value. */ + private float value; + + /** + * Constructs a new MutableFloat with the default value of zero. + */ + public MutableFloat() { + super(); + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store + */ + public MutableFloat(float value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableFloat(Number value) { + super(); + this.value = value.floatValue(); + } + + /** + * Constructs a new MutableFloat parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a float + * @since 2.5 + */ + public MutableFloat(String value) throws NumberFormatException { + super(); + this.value = Float.parseFloat(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Float instance. + * + * @return the value as a Float, never null + */ + public Float getValue() { + return Float.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(float value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.floatValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the float value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Float.isNaN(value); + } + + /** + * Checks whether the float value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Float.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(float operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.floatValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract + * @since Commons Lang 2.2 + */ + public void subtract(float operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.floatValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableFloat as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableFloat as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableFloat as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableFloat as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Float. + * + * @return a Float instance containing the value from this mutable, never null + */ + public Float toFloat() { + return Float.valueOf(floatValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against some other object. The result is <code>true</code> if and only if the argument is + * not <code>null</code> and is a <code>Float</code> object that represents a <code>float</code> that has the + * identical bit pattern to the bit pattern of the <code>float</code> represented by this object. For this + * purpose, two float values are considered to be the same if and only if the method + * {@link Float#floatToIntBits(float)}returns the same int value when applied to each. + * <p> + * Note that in most cases, for two instances of class <code>Float</code>,<code>f1</code> and <code>f2</code>, + * the value of <code>f1.equals(f2)</code> is <code>true</code> if and only if <blockquote> + * + * <pre> + * f1.floatValue() == f2.floatValue() + * </pre> + * + * </blockquote> + * <p> + * also has the value <code>true</code>. However, there are two exceptions: + * <ul> + * <li>If <code>f1</code> and <code>f2</code> both represent <code>Float.NaN</code>, then the + * <code>equals</code> method returns <code>true</code>, even though <code>Float.NaN==Float.NaN</code> has + * the value <code>false</code>. + * <li>If <code>f1</code> represents <code>+0.0f</code> while <code>f2</code> represents <code>-0.0f</code>, + * or vice versa, the <code>equal</code> test has the value <code>false</code>, even though + * <code>0.0f==-0.0f</code> has the value <code>true</code>. + * </ul> + * This definition allows hashtables to operate properly. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + * @see java.lang.Float#floatToIntBits(float) + */ + @Override + public boolean equals(Object obj) { + return (obj instanceof MutableFloat) + && (Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value)); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return Float.floatToIntBits(value); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableFloat other) { + float anotherVal = other.value; + return Float.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableInt.java b/src/org/apache/commons/lang3/mutable/MutableInt.java index eb12dad..03c0092 100644 --- a/src/org/apache/commons/lang3/mutable/MutableInt.java +++ b/src/org/apache/commons/lang3/mutable/MutableInt.java @@ -1,273 +1,273 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>int</code> wrapper.
- * <p>
- * Note that as MutableInt does not extend Integer, it is not treated by String.format as an Integer parameter.
- *
- * @see Integer
- * @since 2.1
- * @version $Id: MutableInt.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableInt extends Number implements Comparable<MutableInt>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 512176391864L;
-
- /** The mutable value. */
- private int value;
-
- /**
- * Constructs a new MutableInt with the default value of zero.
- */
- public MutableInt() {
- super();
- }
-
- /**
- * Constructs a new MutableInt with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableInt(int value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableInt with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableInt(Number value) {
- super();
- this.value = value.intValue();
- }
-
- /**
- * Constructs a new MutableInt parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into an int
- * @since 2.5
- */
- public MutableInt(String value) throws NumberFormatException {
- super();
- this.value = Integer.parseInt(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Integer instance.
- *
- * @return the value as a Integer, never null
- */
- public Integer getValue() {
- return Integer.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(int value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.intValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @since Commons Lang 2.2
- */
- public void add(int operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.intValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @since Commons Lang 2.2
- */
- public void subtract(int operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.intValue();
- }
-
- //-----------------------------------------------------------------------
- // shortValue and byteValue rely on Number implementation
- /**
- * Returns the value of this MutableInt as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableInt as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableInt as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableInt as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Integer.
- *
- * @return a Integer instance containing the value from this mutable, never null
- */
- public Integer toInteger() {
- return Integer.valueOf(intValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is
- * not <code>null</code> and is a <code>MutableInt</code> object that contains the same <code>int</code> value
- * as this object.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MutableInt) {
- return value == ((MutableInt) obj).intValue();
- }
- return false;
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableInt other) {
- int anotherVal = other.value;
- return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>int</code> wrapper. + * <p> + * Note that as MutableInt does not extend Integer, it is not treated by String.format as an Integer parameter. + * + * @see Integer + * @since 2.1 + * @version $Id: MutableInt.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableInt extends Number implements Comparable<MutableInt>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 512176391864L; + + /** The mutable value. */ + private int value; + + /** + * Constructs a new MutableInt with the default value of zero. + */ + public MutableInt() { + super(); + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store + */ + public MutableInt(int value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableInt(Number value) { + super(); + this.value = value.intValue(); + } + + /** + * Constructs a new MutableInt parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into an int + * @since 2.5 + */ + public MutableInt(String value) throws NumberFormatException { + super(); + this.value = Integer.parseInt(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Integer instance. + * + * @return the value as a Integer, never null + */ + public Integer getValue() { + return Integer.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(int value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.intValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(int operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.intValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(int operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.intValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableInt as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Integer. + * + * @return a Integer instance containing the value from this mutable, never null + */ + public Integer toInteger() { + return Integer.valueOf(intValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is <code>true</code> if and only if the argument is + * not <code>null</code> and is a <code>MutableInt</code> object that contains the same <code>int</code> value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableInt) { + return value == ((MutableInt) obj).intValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableInt other) { + int anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableLong.java b/src/org/apache/commons/lang3/mutable/MutableLong.java index eea862d..a4bc52e 100644 --- a/src/org/apache/commons/lang3/mutable/MutableLong.java +++ b/src/org/apache/commons/lang3/mutable/MutableLong.java @@ -1,273 +1,273 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>long</code> wrapper.
- * <p>
- * Note that as MutableLong does not extend Long, it is not treated by String.format as a Long parameter.
- *
- * @see Long
- * @since 2.1
- * @version $Id: MutableLong.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableLong extends Number implements Comparable<MutableLong>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 62986528375L;
-
- /** The mutable value. */
- private long value;
-
- /**
- * Constructs a new MutableLong with the default value of zero.
- */
- public MutableLong() {
- super();
- }
-
- /**
- * Constructs a new MutableLong with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableLong(long value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableLong with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableLong(Number value) {
- super();
- this.value = value.longValue();
- }
-
- /**
- * Constructs a new MutableLong parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into a long
- * @since 2.5
- */
- public MutableLong(String value) throws NumberFormatException {
- super();
- this.value = Long.parseLong(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Long instance.
- *
- * @return the value as a Long, never null
- */
- public Long getValue() {
- return Long.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(long value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.longValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @since Commons Lang 2.2
- */
- public void add(long operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.longValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @since Commons Lang 2.2
- */
- public void subtract(long operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.longValue();
- }
-
- //-----------------------------------------------------------------------
- // shortValue and byteValue rely on Number implementation
- /**
- * Returns the value of this MutableLong as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return (int) value;
- }
-
- /**
- * Returns the value of this MutableLong as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableLong as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableLong as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Long.
- *
- * @return a Long instance containing the value from this mutable, never null
- */
- public Long toLong() {
- return Long.valueOf(longValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object to the specified object. The result is <code>true</code> if and only if the argument
- * is not <code>null</code> and is a <code>MutableLong</code> object that contains the same <code>long</code>
- * value as this object.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MutableLong) {
- return value == ((MutableLong) obj).longValue();
- }
- return false;
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- return (int) (value ^ (value >>> 32));
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableLong other) {
- long anotherVal = other.value;
- return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>long</code> wrapper. + * <p> + * Note that as MutableLong does not extend Long, it is not treated by String.format as a Long parameter. + * + * @see Long + * @since 2.1 + * @version $Id: MutableLong.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableLong extends Number implements Comparable<MutableLong>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 62986528375L; + + /** The mutable value. */ + private long value; + + /** + * Constructs a new MutableLong with the default value of zero. + */ + public MutableLong() { + super(); + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store + */ + public MutableLong(long value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableLong(Number value) { + super(); + this.value = value.longValue(); + } + + /** + * Constructs a new MutableLong parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a long + * @since 2.5 + */ + public MutableLong(String value) throws NumberFormatException { + super(); + this.value = Long.parseLong(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Long instance. + * + * @return the value as a Long, never null + */ + public Long getValue() { + return Long.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(long value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.longValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(long operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.longValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(long operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.longValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableLong as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableLong as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Long. + * + * @return a Long instance containing the value from this mutable, never null + */ + public Long toLong() { + return Long.valueOf(longValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is <code>true</code> if and only if the argument + * is not <code>null</code> and is a <code>MutableLong</code> object that contains the same <code>long</code> + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableLong) { + return value == ((MutableLong) obj).longValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return (int) (value ^ (value >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableLong other) { + long anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableObject.java b/src/org/apache/commons/lang3/mutable/MutableObject.java index 74c4f35..d0deb27 100644 --- a/src/org/apache/commons/lang3/mutable/MutableObject.java +++ b/src/org/apache/commons/lang3/mutable/MutableObject.java @@ -1,126 +1,126 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.commons.lang3.mutable;
-
-import java.io.Serializable;
-
-/**
- * A mutable <code>Object</code> wrapper.
- *
- * @since 2.1
- * @version $Id: MutableObject.java 1088899 2011-04-05 05:31:27Z bayard $
- */
-public class MutableObject<T> implements Mutable<T>, Serializable {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = 86241875189L;
-
- /** The mutable value. */
- private T value;
-
- /**
- * Constructs a new MutableObject with the default value of <code>null</code>.
- */
- public MutableObject() {
- super();
- }
-
- /**
- * Constructs a new MutableObject with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableObject(T value) {
- super();
- this.value = value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value.
- *
- * @return the value, may be null
- */
- public T getValue() {
- return this.value;
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(T value) {
- this.value = value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>
- * Compares this object against the specified object. The result is <code>true</code> if and only if the argument
- * is not <code>null</code> and is a <code>MutableObject</code> object that contains the same <code>T</code>
- * value as this object.
- * </p>
- *
- * @param obj the object to compare with, <code>null</code> returns <code>false</code>
- * @return <code>true</code> if the objects are the same;
- * <code>true</code> if the objects have equivalent <code>value</code> fields;
- * <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj == null) {
- return false;
- }
- if (this == obj) {
- return true;
- }
- if (this.getClass() == obj.getClass()) {
- MutableObject<?> that = (MutableObject<?>) obj;
- return this.value.equals(that.value);
- } else {
- return false;
- }
- }
-
- /**
- * Returns the value's hash code or <code>0</code> if the value is <code>null</code>.
- *
- * @return the value's hash code or <code>0</code> if the value is <code>null</code>.
- */
- @Override
- public int hashCode() {
- return value == null ? 0 : value.hashCode();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return value == null ? "null" : value.toString();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +import java.io.Serializable; + +/** + * A mutable <code>Object</code> wrapper. + * + * @since 2.1 + * @version $Id: MutableObject.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class MutableObject<T> implements Mutable<T>, Serializable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 86241875189L; + + /** The mutable value. */ + private T value; + + /** + * Constructs a new MutableObject with the default value of <code>null</code>. + */ + public MutableObject() { + super(); + } + + /** + * Constructs a new MutableObject with the specified value. + * + * @param value the initial value to store + */ + public MutableObject(T value) { + super(); + this.value = value; + } + + //----------------------------------------------------------------------- + /** + * Gets the value. + * + * @return the value, may be null + */ + public T getValue() { + return this.value; + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(T value) { + this.value = value; + } + + //----------------------------------------------------------------------- + /** + * <p> + * Compares this object against the specified object. The result is <code>true</code> if and only if the argument + * is not <code>null</code> and is a <code>MutableObject</code> object that contains the same <code>T</code> + * value as this object. + * </p> + * + * @param obj the object to compare with, <code>null</code> returns <code>false</code> + * @return <code>true</code> if the objects are the same; + * <code>true</code> if the objects have equivalent <code>value</code> fields; + * <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (this.getClass() == obj.getClass()) { + MutableObject<?> that = (MutableObject<?>) obj; + return this.value.equals(that.value); + } else { + return false; + } + } + + /** + * Returns the value's hash code or <code>0</code> if the value is <code>null</code>. + * + * @return the value's hash code or <code>0</code> if the value is <code>null</code>. + */ + @Override + public int hashCode() { + return value == null ? 0 : value.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return value == null ? "null" : value.toString(); + } + +} diff --git a/src/org/apache/commons/lang3/mutable/MutableShort.java b/src/org/apache/commons/lang3/mutable/MutableShort.java index 2dfcb00..33ca923 100644 --- a/src/org/apache/commons/lang3/mutable/MutableShort.java +++ b/src/org/apache/commons/lang3/mutable/MutableShort.java @@ -1,283 +1,283 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.mutable;
-
-/**
- * A mutable <code>short</code> wrapper.
- * <p>
- * Note that as MutableShort does not extend Short, it is not treated by String.format as a Short parameter.
- *
- * @see Short
- * @since 2.1
- * @version $Id: MutableShort.java 1160571 2011-08-23 07:36:08Z bayard $
- */
-public class MutableShort extends Number implements Comparable<MutableShort>, Mutable<Number> {
-
- /**
- * Required for serialization support.
- *
- * @see java.io.Serializable
- */
- private static final long serialVersionUID = -2135791679L;
-
- /** The mutable value. */
- private short value;
-
- /**
- * Constructs a new MutableShort with the default value of zero.
- */
- public MutableShort() {
- super();
- }
-
- /**
- * Constructs a new MutableShort with the specified value.
- *
- * @param value the initial value to store
- */
- public MutableShort(short value) {
- super();
- this.value = value;
- }
-
- /**
- * Constructs a new MutableShort with the specified value.
- *
- * @param value the initial value to store, not null
- * @throws NullPointerException if the object is null
- */
- public MutableShort(Number value) {
- super();
- this.value = value.shortValue();
- }
-
- /**
- * Constructs a new MutableShort parsing the given string.
- *
- * @param value the string to parse, not null
- * @throws NumberFormatException if the string cannot be parsed into a short
- * @since 2.5
- */
- public MutableShort(String value) throws NumberFormatException {
- super();
- this.value = Short.parseShort(value);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets the value as a Short instance.
- *
- * @return the value as a Short, never null
- */
- public Short getValue() {
- return Short.valueOf(this.value);
- }
-
- /**
- * Sets the value.
- *
- * @param value the value to set
- */
- public void setValue(short value) {
- this.value = value;
- }
-
- /**
- * Sets the value from any Number instance.
- *
- * @param value the value to set, not null
- * @throws NullPointerException if the object is null
- */
- public void setValue(Number value) {
- this.value = value.shortValue();
- }
-
- //-----------------------------------------------------------------------
- /**
- * Increments the value.
- *
- * @since Commons Lang 2.2
- */
- public void increment() {
- value++;
- }
-
- /**
- * Decrements the value.
- *
- * @since Commons Lang 2.2
- */
- public void decrement() {
- value--;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @since Commons Lang 2.2
- */
- public void add(short operand) {
- this.value += operand;
- }
-
- /**
- * Adds a value to the value of this instance.
- *
- * @param operand the value to add, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void add(Number operand) {
- this.value += operand.shortValue();
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @since Commons Lang 2.2
- */
- public void subtract(short operand) {
- this.value -= operand;
- }
-
- /**
- * Subtracts a value from the value of this instance.
- *
- * @param operand the value to subtract, not null
- * @throws NullPointerException if the object is null
- * @since Commons Lang 2.2
- */
- public void subtract(Number operand) {
- this.value -= operand.shortValue();
- }
-
- //-----------------------------------------------------------------------
- // byteValue relies on Number implementation
- /**
- * Returns the value of this MutableShort as a short.
- *
- * @return the numeric value represented by this object after conversion to type short.
- */
- @Override
- public short shortValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableShort as an int.
- *
- * @return the numeric value represented by this object after conversion to type int.
- */
- @Override
- public int intValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableShort as a long.
- *
- * @return the numeric value represented by this object after conversion to type long.
- */
- @Override
- public long longValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableShort as a float.
- *
- * @return the numeric value represented by this object after conversion to type float.
- */
- @Override
- public float floatValue() {
- return value;
- }
-
- /**
- * Returns the value of this MutableShort as a double.
- *
- * @return the numeric value represented by this object after conversion to type double.
- */
- @Override
- public double doubleValue() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Gets this mutable as an instance of Short.
- *
- * @return a Short instance containing the value from this mutable, never null
- */
- public Short toShort() {
- return Short.valueOf(shortValue());
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this object to the specified object. The result is <code>true</code> if and only if the argument
- * is not <code>null</code> and is a <code>MutableShort</code> object that contains the same <code>short</code>
- * value as this object.
- *
- * @param obj the object to compare with, null returns false
- * @return <code>true</code> if the objects are the same; <code>false</code> otherwise.
- */
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof MutableShort) {
- return value == ((MutableShort) obj).shortValue();
- }
- return false;
- }
-
- /**
- * Returns a suitable hash code for this mutable.
- *
- * @return a suitable hash code
- */
- @Override
- public int hashCode() {
- return value;
- }
-
- //-----------------------------------------------------------------------
- /**
- * Compares this mutable to another in ascending order.
- *
- * @param other the other mutable to compare to, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(MutableShort other) {
- short anotherVal = other.value;
- return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1);
- }
-
- //-----------------------------------------------------------------------
- /**
- * Returns the String value of this mutable.
- *
- * @return the mutable value as a string
- */
- @Override
- public String toString() {
- return String.valueOf(value);
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable <code>short</code> wrapper. + * <p> + * Note that as MutableShort does not extend Short, it is not treated by String.format as a Short parameter. + * + * @see Short + * @since 2.1 + * @version $Id: MutableShort.java 1160571 2011-08-23 07:36:08Z bayard $ + */ +public class MutableShort extends Number implements Comparable<MutableShort>, Mutable<Number> { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -2135791679L; + + /** The mutable value. */ + private short value; + + /** + * Constructs a new MutableShort with the default value of zero. + */ + public MutableShort() { + super(); + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store + */ + public MutableShort(short value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableShort(Number value) { + super(); + this.value = value.shortValue(); + } + + /** + * Constructs a new MutableShort parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a short + * @since 2.5 + */ + public MutableShort(String value) throws NumberFormatException { + super(); + this.value = Short.parseShort(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Short instance. + * + * @return the value as a Short, never null + */ + public Short getValue() { + return Short.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(short value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + public void setValue(Number value) { + this.value = value.shortValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(short operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.shortValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(short operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.shortValue(); + } + + //----------------------------------------------------------------------- + // byteValue relies on Number implementation + /** + * Returns the value of this MutableShort as a short. + * + * @return the numeric value represented by this object after conversion to type short. + */ + @Override + public short shortValue() { + return value; + } + + /** + * Returns the value of this MutableShort as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Short. + * + * @return a Short instance containing the value from this mutable, never null + */ + public Short toShort() { + return Short.valueOf(shortValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is <code>true</code> if and only if the argument + * is not <code>null</code> and is a <code>MutableShort</code> object that contains the same <code>short</code> + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return <code>true</code> if the objects are the same; <code>false</code> otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableShort) { + return value == ((MutableShort) obj).shortValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(MutableShort other) { + short anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/src/org/apache/commons/lang3/tuple/ImmutablePair.java b/src/org/apache/commons/lang3/tuple/ImmutablePair.java index 8e88a43..61eee5b 100644 --- a/src/org/apache/commons/lang3/tuple/ImmutablePair.java +++ b/src/org/apache/commons/lang3/tuple/ImmutablePair.java @@ -1,103 +1,103 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.tuple;
-
-/**
- * <p>An immutable pair consisting of two {@code Object} elements.</p>
- *
- * <p>Although the implementation is immutable, there is no restriction on the objects
- * that may be stored. If mutable objects are stored in the pair, then the pair
- * itself effectively becomes mutable. The class is also not {@code final}, so a subclass
- * could add undesirable behaviour.</p>
- *
- * <p>#ThreadSafe# if the objects are threadsafe</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- *
- * @since Lang 3.0
- * @version $Id: ImmutablePair.java 1127544 2011-05-25 14:35:42Z scolebourne $
- */
-public final class ImmutablePair<L, R> extends Pair<L, R> {
-
- /** Serialization version */
- private static final long serialVersionUID = 4954918890077093841L;
-
- /** Left object */
- public final L left;
- /** Right object */
- public final R right;
-
- /**
- * <p>Obtains an immutable pair of from two objects inferring the generic types.</p>
- *
- * <p>This factory allows the pair to be created using inference to
- * obtain the generic types.</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- * @param left the left element, may be null
- * @param right the right element, may be null
- * @return a pair formed from the two parameters, not null
- */
- public static <L, R> ImmutablePair<L, R> of(L left, R right) {
- return new ImmutablePair<L, R>(left, right);
- }
-
- /**
- * Create a new pair instance.
- *
- * @param left the left value, may be null
- * @param right the right value, may be null
- */
- public ImmutablePair(L left, R right) {
- super();
- this.left = left;
- this.right = right;
- }
-
- //-----------------------------------------------------------------------
- /**
- * {@inheritDoc}
- */
- @Override
- public L getLeft() {
- return left;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public R getRight() {
- return right;
- }
-
- /**
- * <p>Throws {@code UnsupportedOperationException}.</p>
- *
- * <p>This pair is immutable, so this operation is not supported.</p>
- *
- * @param value the value to set
- * @return never
- * @throws UnsupportedOperationException as this operation is not supported
- */
- public R setValue(R value) {
- throw new UnsupportedOperationException();
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.tuple; + +/** + * <p>An immutable pair consisting of two {@code Object} elements.</p> + * + * <p>Although the implementation is immutable, there is no restriction on the objects + * that may be stored. If mutable objects are stored in the pair, then the pair + * itself effectively becomes mutable. The class is also not {@code final}, so a subclass + * could add undesirable behaviour.</p> + * + * <p>#ThreadSafe# if the objects are threadsafe</p> + * + * @param <L> the left element type + * @param <R> the right element type + * + * @since Lang 3.0 + * @version $Id: ImmutablePair.java 1127544 2011-05-25 14:35:42Z scolebourne $ + */ +public final class ImmutablePair<L, R> extends Pair<L, R> { + + /** Serialization version */ + private static final long serialVersionUID = 4954918890077093841L; + + /** Left object */ + public final L left; + /** Right object */ + public final R right; + + /** + * <p>Obtains an immutable pair of from two objects inferring the generic types.</p> + * + * <p>This factory allows the pair to be created using inference to + * obtain the generic types.</p> + * + * @param <L> the left element type + * @param <R> the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static <L, R> ImmutablePair<L, R> of(L left, R right) { + return new ImmutablePair<L, R>(left, right); + } + + /** + * Create a new pair instance. + * + * @param left the left value, may be null + * @param right the right value, may be null + */ + public ImmutablePair(L left, R right) { + super(); + this.left = left; + this.right = right; + } + + //----------------------------------------------------------------------- + /** + * {@inheritDoc} + */ + @Override + public L getLeft() { + return left; + } + + /** + * {@inheritDoc} + */ + @Override + public R getRight() { + return right; + } + + /** + * <p>Throws {@code UnsupportedOperationException}.</p> + * + * <p>This pair is immutable, so this operation is not supported.</p> + * + * @param value the value to set + * @return never + * @throws UnsupportedOperationException as this operation is not supported + */ + public R setValue(R value) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/org/apache/commons/lang3/tuple/MutablePair.java b/src/org/apache/commons/lang3/tuple/MutablePair.java index 9864c02..df5c53f 100644 --- a/src/org/apache/commons/lang3/tuple/MutablePair.java +++ b/src/org/apache/commons/lang3/tuple/MutablePair.java @@ -1,123 +1,123 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.tuple;
-
-/**
- * <p>A mutable pair consisting of two {@code Object} elements.</p>
- *
- * <p>Not #ThreadSafe#</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- *
- * @since Lang 3.0
- * @version $Id: MutablePair.java 1127544 2011-05-25 14:35:42Z scolebourne $
- */
-public class MutablePair<L, R> extends Pair<L, R> {
-
- /** Serialization version */
- private static final long serialVersionUID = 4954918890077093841L;
-
- /** Left object */
- public L left;
- /** Right object */
- public R right;
-
- /**
- * <p>Obtains an immutable pair of from two objects inferring the generic types.</p>
- *
- * <p>This factory allows the pair to be created using inference to
- * obtain the generic types.</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- * @param left the left element, may be null
- * @param right the right element, may be null
- * @return a pair formed from the two parameters, not null
- */
- public static <L, R> MutablePair<L, R> of(L left, R right) {
- return new MutablePair<L, R>(left, right);
- }
-
- /**
- * Create a new pair instance of two nulls.
- */
- public MutablePair() {
- super();
- }
-
- /**
- * Create a new pair instance.
- *
- * @param left the left value, may be null
- * @param right the right value, may be null
- */
- public MutablePair(L left, R right) {
- super();
- this.left = left;
- this.right = right;
- }
-
- //-----------------------------------------------------------------------
- /**
- * {@inheritDoc}
- */
- @Override
- public L getLeft() {
- return left;
- }
-
- /**
- * Sets the left element of the pair.
- *
- * @param left the new value of the left element, may be null
- */
- public void setLeft(L left) {
- this.left = left;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public R getRight() {
- return right;
- }
-
- /**
- * Sets the right element of the pair.
- *
- * @param right the new value of the right element, may be null
- */
- public void setRight(R right) {
- this.right = right;
- }
-
- /**
- * Sets the {@code Map.Entry} value.
- * This sets the right element of the pair.
- *
- * @param value the right value to set, not null
- * @return the old value for the right element
- */
- public R setValue(R value) {
- R result = getRight();
- setRight(value);
- return result;
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.tuple; + +/** + * <p>A mutable pair consisting of two {@code Object} elements.</p> + * + * <p>Not #ThreadSafe#</p> + * + * @param <L> the left element type + * @param <R> the right element type + * + * @since Lang 3.0 + * @version $Id: MutablePair.java 1127544 2011-05-25 14:35:42Z scolebourne $ + */ +public class MutablePair<L, R> extends Pair<L, R> { + + /** Serialization version */ + private static final long serialVersionUID = 4954918890077093841L; + + /** Left object */ + public L left; + /** Right object */ + public R right; + + /** + * <p>Obtains an immutable pair of from two objects inferring the generic types.</p> + * + * <p>This factory allows the pair to be created using inference to + * obtain the generic types.</p> + * + * @param <L> the left element type + * @param <R> the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static <L, R> MutablePair<L, R> of(L left, R right) { + return new MutablePair<L, R>(left, right); + } + + /** + * Create a new pair instance of two nulls. + */ + public MutablePair() { + super(); + } + + /** + * Create a new pair instance. + * + * @param left the left value, may be null + * @param right the right value, may be null + */ + public MutablePair(L left, R right) { + super(); + this.left = left; + this.right = right; + } + + //----------------------------------------------------------------------- + /** + * {@inheritDoc} + */ + @Override + public L getLeft() { + return left; + } + + /** + * Sets the left element of the pair. + * + * @param left the new value of the left element, may be null + */ + public void setLeft(L left) { + this.left = left; + } + + /** + * {@inheritDoc} + */ + @Override + public R getRight() { + return right; + } + + /** + * Sets the right element of the pair. + * + * @param right the new value of the right element, may be null + */ + public void setRight(R right) { + this.right = right; + } + + /** + * Sets the {@code Map.Entry} value. + * This sets the right element of the pair. + * + * @param value the right value to set, not null + * @return the old value for the right element + */ + public R setValue(R value) { + R result = getRight(); + setRight(value); + return result; + } + +} diff --git a/src/org/apache/commons/lang3/tuple/Pair.java b/src/org/apache/commons/lang3/tuple/Pair.java index cd24e79..dc9a045 100644 --- a/src/org/apache/commons/lang3/tuple/Pair.java +++ b/src/org/apache/commons/lang3/tuple/Pair.java @@ -1,176 +1,176 @@ -/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.commons.lang3.tuple;
-
-import java.io.Serializable;
-import java.util.Map;
-
-import org.apache.commons.lang3.ObjectUtils;
-import org.apache.commons.lang3.builder.CompareToBuilder;
-
-/**
- * <p>A pair consisting of two elements.</p>
- *
- * <p>This class is an abstract implementation defining the basic API.
- * It refers to the elements as 'left' and 'right'. It also implements the
- * {@code Map.Entry} interface where the key is 'left' and the value is 'right'.</p>
- *
- * <p>Subclass implementations may be mutable or immutable.
- * However, there is no restriction on the type of the stored objects that may be stored.
- * If mutable objects are stored in the pair, then the pair itself effectively becomes mutable.</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- *
- * @since Lang 3.0
- * @version $Id: Pair.java 1142401 2011-07-03 08:30:12Z bayard $
- */
-public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable {
-
- /** Serialization version */
- private static final long serialVersionUID = 4954918890077093841L;
-
- /**
- * <p>Obtains an immutable pair of from two objects inferring the generic types.</p>
- *
- * <p>This factory allows the pair to be created using inference to
- * obtain the generic types.</p>
- *
- * @param <L> the left element type
- * @param <R> the right element type
- * @param left the left element, may be null
- * @param right the right element, may be null
- * @return a pair formed from the two parameters, not null
- */
- public static <L, R> Pair<L, R> of(L left, R right) {
- return new ImmutablePair<L, R>(left, right);
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Gets the left element from this pair.</p>
- *
- * <p>When treated as a key-value pair, this is the key.</p>
- *
- * @return the left element, may be null
- */
- public abstract L getLeft();
-
- /**
- * <p>Gets the right element from this pair.</p>
- *
- * <p>When treated as a key-value pair, this is the value.</p>
- *
- * @return the right element, may be null
- */
- public abstract R getRight();
-
- /**
- * <p>Gets the key from this pair.</p>
- *
- * <p>This method implements the {@code Map.Entry} interface returning the
- * left element as the key.</p>
- *
- * @return the left element as the key, may be null
- */
- public final L getKey() {
- return getLeft();
- }
-
- /**
- * <p>Gets the value from this pair.</p>
- *
- * <p>This method implements the {@code Map.Entry} interface returning the
- * right element as the value.</p>
- *
- * @return the right element as the value, may be null
- */
- public R getValue() {
- return getRight();
- }
-
- //-----------------------------------------------------------------------
- /**
- * <p>Compares the pair based on the left element followed by the right element.
- * The types must be {@code Comparable}.</p>
- *
- * @param other the other pair, not null
- * @return negative if this is less, zero if equal, positive if greater
- */
- public int compareTo(Pair<L, R> other) {
- return new CompareToBuilder().append(getLeft(), other.getLeft())
- .append(getRight(), other.getRight()).toComparison();
- }
-
- /**
- * <p>Compares this pair to another based on the two elements.</p>
- *
- * @param obj the object to compare to, null returns false
- * @return true if the elements of the pair are equal
- */
- @Override
- public boolean equals(Object obj) {
- if (obj == this) {
- return true;
- }
- if (obj instanceof Map.Entry<?, ?>) {
- Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj;
- return ObjectUtils.equals(getKey(), other.getKey())
- && ObjectUtils.equals(getValue(), other.getValue());
- }
- return false;
- }
-
- /**
- * <p>Returns a suitable hash code.
- * The hash code follows the definition in {@code Map.Entry}.</p>
- *
- * @return the hash code
- */
- @Override
- public int hashCode() {
- // see Map.Entry API specification
- return (getKey() == null ? 0 : getKey().hashCode()) ^
- (getValue() == null ? 0 : getValue().hashCode());
- }
-
- /**
- * <p>Returns a String representation of this pair using the format {@code ($left,$right)}.</p>
- *
- * @return a string describing this object, not null
- */
- @Override
- public String toString() {
- return new StringBuilder().append('(').append(getLeft()).append(',').append(getRight()).append(')').toString();
- }
-
- /**
- * <p>Formats the receiver using the given format.</p>
- *
- * <p>This uses {@link java.util.Formattable} to perform the formatting. Two variables may
- * be used to embed the left and right elements. Use {@code %1$s} for the left
- * element (key) and {@code %2$s} for the right element (value).
- * The default format used by {@code toString()} is {@code (%1$s,%2$s)}.</p>
- *
- * @param format the format string, optionally containing {@code %1$s} and {@code %2$s}, not null
- * @return the formatted string, not null
- */
- public String toString(String format) {
- return String.format(format, getLeft(), getRight());
- }
-
-}
+/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.tuple; + +import java.io.Serializable; +import java.util.Map; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.builder.CompareToBuilder; + +/** + * <p>A pair consisting of two elements.</p> + * + * <p>This class is an abstract implementation defining the basic API. + * It refers to the elements as 'left' and 'right'. It also implements the + * {@code Map.Entry} interface where the key is 'left' and the value is 'right'.</p> + * + * <p>Subclass implementations may be mutable or immutable. + * However, there is no restriction on the type of the stored objects that may be stored. + * If mutable objects are stored in the pair, then the pair itself effectively becomes mutable.</p> + * + * @param <L> the left element type + * @param <R> the right element type + * + * @since Lang 3.0 + * @version $Id: Pair.java 1142401 2011-07-03 08:30:12Z bayard $ + */ +public abstract class Pair<L, R> implements Map.Entry<L, R>, Comparable<Pair<L, R>>, Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 4954918890077093841L; + + /** + * <p>Obtains an immutable pair of from two objects inferring the generic types.</p> + * + * <p>This factory allows the pair to be created using inference to + * obtain the generic types.</p> + * + * @param <L> the left element type + * @param <R> the right element type + * @param left the left element, may be null + * @param right the right element, may be null + * @return a pair formed from the two parameters, not null + */ + public static <L, R> Pair<L, R> of(L left, R right) { + return new ImmutablePair<L, R>(left, right); + } + + //----------------------------------------------------------------------- + /** + * <p>Gets the left element from this pair.</p> + * + * <p>When treated as a key-value pair, this is the key.</p> + * + * @return the left element, may be null + */ + public abstract L getLeft(); + + /** + * <p>Gets the right element from this pair.</p> + * + * <p>When treated as a key-value pair, this is the value.</p> + * + * @return the right element, may be null + */ + public abstract R getRight(); + + /** + * <p>Gets the key from this pair.</p> + * + * <p>This method implements the {@code Map.Entry} interface returning the + * left element as the key.</p> + * + * @return the left element as the key, may be null + */ + public final L getKey() { + return getLeft(); + } + + /** + * <p>Gets the value from this pair.</p> + * + * <p>This method implements the {@code Map.Entry} interface returning the + * right element as the value.</p> + * + * @return the right element as the value, may be null + */ + public R getValue() { + return getRight(); + } + + //----------------------------------------------------------------------- + /** + * <p>Compares the pair based on the left element followed by the right element. + * The types must be {@code Comparable}.</p> + * + * @param other the other pair, not null + * @return negative if this is less, zero if equal, positive if greater + */ + public int compareTo(Pair<L, R> other) { + return new CompareToBuilder().append(getLeft(), other.getLeft()) + .append(getRight(), other.getRight()).toComparison(); + } + + /** + * <p>Compares this pair to another based on the two elements.</p> + * + * @param obj the object to compare to, null returns false + * @return true if the elements of the pair are equal + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Map.Entry<?, ?>) { + Map.Entry<?, ?> other = (Map.Entry<?, ?>) obj; + return ObjectUtils.equals(getKey(), other.getKey()) + && ObjectUtils.equals(getValue(), other.getValue()); + } + return false; + } + + /** + * <p>Returns a suitable hash code. + * The hash code follows the definition in {@code Map.Entry}.</p> + * + * @return the hash code + */ + @Override + public int hashCode() { + // see Map.Entry API specification + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); + } + + /** + * <p>Returns a String representation of this pair using the format {@code ($left,$right)}.</p> + * + * @return a string describing this object, not null + */ + @Override + public String toString() { + return new StringBuilder().append('(').append(getLeft()).append(',').append(getRight()).append(')').toString(); + } + + /** + * <p>Formats the receiver using the given format.</p> + * + * <p>This uses {@link java.util.Formattable} to perform the formatting. Two variables may + * be used to embed the left and right elements. Use {@code %1$s} for the left + * element (key) and {@code %2$s} for the right element (value). + * The default format used by {@code toString()} is {@code (%1$s,%2$s)}.</p> + * + * @param format the format string, optionally containing {@code %1$s} and {@code %2$s}, not null + * @return the formatted string, not null + */ + public String toString(String format) { + return String.format(format, getLeft(), getRight()); + } + +} |
