diff --git a/.classpath b/.classpath index 9b95a10..e86a2df 100644 --- a/.classpath +++ b/.classpath @@ -1,11 +1,7 @@ - - - - - + @@ -16,7 +12,7 @@ - + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cc4d53b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +bin/ +doc/ + +unused/ diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..c2a3300 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,460 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=16 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=16 +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.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deprecation=warning +org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled +org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled +org.eclipse.jdt.core.compiler.problem.discouragedReference=warning +org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore +org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore +org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=error +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=enabled +org.eclipse.jdt.core.compiler.source=16 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=48 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=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=next_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=next_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=next_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=next_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=next_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=next_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=next_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=false +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +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.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +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=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +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_after_type_annotation=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=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=insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=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_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=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_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not 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_switch_case_expressions=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_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=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_try=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_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=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_bitwise_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_try=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_switch_case_expressions=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_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=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_try=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_relational_operator=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_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +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_enum_constant_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=true +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=true +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.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=3 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000..9e24221 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,63 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_NobesFormat-2019Aug +formatter_settings_version=16 +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_functional_interfaces=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=false +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.insert_inferred_type_arguments=false +sp_cleanup.make_local_variable_final=true +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=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +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_redundant_modifiers=false +sp_cleanup.remove_redundant_semicolons=false +sp_cleanup.remove_redundant_type_arguments=false +sp_cleanup.remove_trailing_whitespaces=true +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_anonymous_class_creation=false +sp_cleanup.use_blocks=false +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_lambda=true +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/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..cf0073b --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# GLUM: Great Library of User Modules + + +## Description +GLUM is a Java library that provides a collection of generic components, modules, and utilities which aid in development of Java software applications. + +It provides the following high level capabilities: + +- various components and utilities to simplify the development (creation, layout, organization, setup) of a Swing based user interface (UI) + +- a data model focused framework for the display, editing, filtering, and searching of tabular data + +- capability to monitor, query, or alter a “task” (mechanism to allow an end user to get information, track progress, and abort a process) + +- serialization mechanism for saving and loading of application state + +- capability to configure the output of data values (numeric, textual, date/time, etc) + +- framework to allow retrieval (start, pause, resume) and management of data (local and/or remote) resources + + +## Usage +The latest release of GLUM is 2.0.0 and is distributed as a binary jar (glum-2.0.0.jar) and the corresponding source jar (glum-2.0.0-src.jar). These will need to be added to your class path. + +It is intended that support via Maven mechanism will be provided in a future release. + + +## Dependencies +The GLUM library has the following (linking) dependencies utilized while developing the GLUM library: + +- Java 17 +- Guava: 18.0 +- MigLayout: 3.7.2 +- DockingFrames: 1.1.3 + +In addition, to compile GLUM the following software packages are needed: + +- JDK 17+ +- Python 3.6+ +- Apache Ant 1.10.8+ + +Note the following: + +- In theory GLUM should work with later versions of the above listed software, but these were the ones utilized during the primary development phase. +- The DockingFrames dependency is only necessary if gui docking capabilities are desired. + +## Building GLUM +To build GLUM from the console, run the following command: + +    ./tools/buildRelease diff --git a/release/glum-2.0.0-src.jar b/release/glum-2.0.0-src.jar new file mode 100644 index 0000000..11c3d79 Binary files /dev/null and b/release/glum-2.0.0-src.jar differ diff --git a/release/glum-2.0.0.jar b/release/glum-2.0.0.jar new file mode 100644 index 0000000..fee8bde Binary files /dev/null and b/release/glum-2.0.0.jar differ diff --git a/src/glum/app/AppGlum.java b/src/glum/app/AppGlum.java new file mode 100644 index 0000000..ffdcdf2 --- /dev/null +++ b/src/glum/app/AppGlum.java @@ -0,0 +1,32 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.app; + +/** + * Provides the main entry point. + *

+ * Currently this will just print the library name and the version. + */ +public class AppGlum +{ + /** + * Main entry point that will print out the version of Glum to stdout. + */ + public static void main(String[] aArgArr) + { + System.out.println("Glum Library. Version: " + AppInfo.getVersion()); + System.exit(0); + } + +} diff --git a/src/glum/app/AppInfo.java b/src/glum/app/AppInfo.java new file mode 100644 index 0000000..3c68852 --- /dev/null +++ b/src/glum/app/AppInfo.java @@ -0,0 +1,39 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.app; + +/** + * Immutable class that provides information related to the build. + * + * @author lopeznr1 + */ +public class AppInfo +{ + /** + * Defines the version of the Glum library. + *

+ * Note we do not make the Version directly visible and final so other classes will not utilized a cached version + * when built via Ant. + */ + private static String Version = "2.0.0"; + + /** + * Returns the version of the application + */ + public static String getVersion() + { + return Version; + } + +} diff --git a/src/glum/color/ColorHSBL.java b/src/glum/color/ColorHSBL.java new file mode 100644 index 0000000..21a1d99 --- /dev/null +++ b/src/glum/color/ColorHSBL.java @@ -0,0 +1,121 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.color; + +import java.awt.Color; + +/** + * Immutable object which provides access to attributes of a color. + *

+ * The attributes are: hue, saturation, brightness, luminance + * + * @author lopeznr1 + */ +public class ColorHSBL +{ + // Attributes + private final Color refColor; + private final float hue; + private final float saturation; + private final float brightness; + private final float luminance; + + /** Standard Constructor */ + public ColorHSBL(Color aColor) + { + refColor = aColor; + + float[] tmpArr = convertColorToHSBL(aColor); + hue = tmpArr[0]; + saturation = tmpArr[1]; + brightness = tmpArr[2]; + luminance = tmpArr[3]; + } + + /** + * Returns the associated {@link Color}. + */ + public Color getColor() + { + return refColor; + } + + /** + * Returns the hue component. + */ + public float getHue() + { + return hue; + } + + /** + * Returns the saturation component. + */ + public float getSaturation() + { + return saturation; + } + + /** + * Returns the brightness component. + */ + public float getBrightness() + { + return brightness; + } + + /** + * Returns the luminance component. + */ + public float getLuminance() + { + return luminance; + } + + /** + * Utility method that returns a 4 element array that has the following in the elements + *

    + *
  • hue + *
  • saturation + *
  • brightness + *
  • luminosity + *
+ * The source for this conversion originated from:
+ * https://stackoverflow.com/questions/596216/formula-to-determine-perceived-brightness-of-rgb-color + *

+ * Note the above source has changed overtime and the conversion below is not authoritative. + */ + public static float[] convertColorToHSBL(Color aColor) + { + int rI = aColor.getRed(); + int gI = aColor.getGreen(); + int bI = aColor.getBlue(); + + float rF = rI / 255.0f; + float gF = gI / 255.0f; + float bF = bI / 255.0f; + // Formula: broken +// float lum = (float) Math.sqrt(0.241f * rF + 0.691f * gF + 0.068f * bF); + // Formula: corrected +// float lum = (0.299f * rF + 0.587f * gF + 0.114f * bF); + float lum = (float) Math.sqrt(0.299f * rF * rF + 0.587f * gF * gF + 0.114f * bF * bF); + + float[] hsblArr = new float[4]; + Color.RGBtoHSB(rI, gI, bI, hsblArr); + + hsblArr[3] = lum; + return hsblArr; + } + +} diff --git a/src/glum/color/ColorHSBLCompartor.java b/src/glum/color/ColorHSBLCompartor.java new file mode 100644 index 0000000..33835d8 --- /dev/null +++ b/src/glum/color/ColorHSBLCompartor.java @@ -0,0 +1,125 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.color; + +import java.util.Comparator; +import java.util.List; + +import com.google.common.collect.ImmutableList; + +/** + * Flexible Color Comparator that provides color comparisons using any combination of the attributes HLSB (hue, lumance, + * saturation, brightness) rather than RGB (red,green, blue). + * + * @author lopeznr1 + */ +public class ColorHSBLCompartor implements Comparator +{ + // Constants + private static ImmutableList SortKey_HLB = ImmutableList.of(KeyAttr.Hue, KeyAttr.Luminance, + KeyAttr.Brightness); + + // Attributes + private final List keyL; + private final int numGroups; + + /** + * Standard Constructor + * + * @param aKeyL + * Defines an ordered list of {@link KeyAttr}s that will define how colors are sorted. + * @param aNumGroups + * Defines the number of groups for which the various color attributes will be grouped to. + */ + public ColorHSBLCompartor(List aKeyL, int aNumGroups) + { + keyL = ImmutableList.copyOf(aKeyL); + numGroups = aNumGroups; + } + + /** + * Simplified Constructor + * + * Colors will be sorted by the following ordered attributes: hue, lumance, and brightness. + * + * @param aNumGroups + * Defines the number of groups for which the various color attributes will be grouped to. + */ + public ColorHSBLCompartor(int aNumGroups) + { + this(SortKey_HLB, aNumGroups); + } + + @Override + public int compare(ColorHSBL aHsbl1, ColorHSBL aHsbl2) + { + int prevStep = 0; + + // Iterate through all of the keys + int tmpIdx = -1; + for (KeyAttr aKey : keyL) + { + tmpIdx++; + double fVal1, fVal2; + switch (aKey) + { + case Hue: + fVal1 = aHsbl1.getHue(); + fVal2 = aHsbl2.getHue(); + break; + case Saturation: + fVal1 = aHsbl1.getSaturation(); + fVal2 = aHsbl2.getSaturation(); + break; + case Brightness: + fVal1 = aHsbl1.getBrightness(); + fVal2 = aHsbl2.getBrightness(); + break; + case Luminance: + fVal1 = aHsbl1.getLuminance(); + fVal2 = aHsbl2.getLuminance(); + break; + + default: + throw new UnsupportedOperationException("Unrecognized key: " + aKey); + } + + // Compare the corresponding normalized (group) values + int iVal1 = (int) (fVal1 * numGroups); + int iVal2 = (int) (fVal2 * numGroups); + int cmpVal = Integer.compare(iVal1, iVal2); + + // If we are at the last key - do not use the normalize (group) values + // but rather utilize the non-normalized raw float values so as to + // reduce (eliminate?) ambiguities due to values being in the same bin. + if (tmpIdx == keyL.size() - 1) + cmpVal = Double.compare(fVal1, fVal2); + + // Note we flip the comparison if the previous (key) comparison + // resulted in an odd value. We do this so that rather than having + // a number of step function we get more of an oscillating smooth + // transition between the comparison keys. + if (prevStep % 2 == 1) + cmpVal = -cmpVal; + + if (cmpVal != 0) + return cmpVal; + + prevStep = iVal1; + } + + return 0; + } + +} diff --git a/src/glum/color/ColorTestApp.java b/src/glum/color/ColorTestApp.java new file mode 100644 index 0000000..283c034 --- /dev/null +++ b/src/glum/color/ColorTestApp.java @@ -0,0 +1,261 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.color; + +import java.awt.Color; +import java.util.*; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import com.google.common.collect.ImmutableList; + +import glum.gui.misc.BooleanCellEditor; +import glum.gui.misc.BooleanCellRenderer; +import glum.gui.panel.itemList.*; +import glum.gui.panel.itemList.query.QueryComposer; +import glum.gui.table.ColorHSBLCellRenderer; +import glum.gui.table.PrePendRenderer; +import net.miginfocom.swing.MigLayout; + +/** + * Demo application that demonstrates a number of features: + *

    + *
  • Showing color attributes via a table + *
  • Alternative color sorting + *
  • Multicolmun sorting + *
+ * + * @author lopeznr1 + */ +public class ColorTestApp +{ + // Constants + private static final int DefNumGroups = 8; +// private static final int DefNumItems = 100; // 3100 + private static final int DefNumItems = 3100; + + /** + * Main entry point. + */ + public static void main(String[] args) + { + JFrame frame = new JFrame("TableColorTest"); + frame.setContentPane(formTestPanel(DefNumGroups, DefNumItems)); + + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setVisible(true); + } + + private static JPanel formTestPanel(int aNumGroups, int aNumItems) + { + ItemListPanel tmpILP; + + JPanel retPanel = new JPanel(new MigLayout()); + + // Table Content + QueryComposer tmpComposer = new QueryComposer<>(); + tmpComposer.addAttribute(LookUp.IsVisible, Boolean.class, "Show", 50); + tmpComposer.addAttribute(LookUp.Index, Integer.class, "ID", "Trk: 99"); + + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "HBL", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "HLB", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "HLS", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "HSB", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "HSL", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "SBH", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "SBL", 50); + tmpComposer.addAttribute(LookUp.HSBL, Color.class, "SLB", 50); + + tmpComposer.addAttribute(LookUp.Message, String.class, "Message", null); + tmpComposer.addAttribute(LookUp.Red, Integer.class, "Red", 40); + tmpComposer.addAttribute(LookUp.Green, Integer.class, "Green", 40); + tmpComposer.addAttribute(LookUp.Blue, Integer.class, "Blue", 40); + tmpComposer.addAttribute(LookUp.Hue, Integer.class, "Hue:" + aNumGroups, 60); + tmpComposer.addAttribute(LookUp.Sat, Integer.class, "Sat:" + aNumGroups, 60); + tmpComposer.addAttribute(LookUp.Lum, Integer.class, "Lum:" + aNumGroups, 60); + tmpComposer.addAttribute(LookUp.Bri, Integer.class, "Bri:" + aNumGroups, 60); + + tmpComposer.setEditor(LookUp.IsVisible, new BooleanCellEditor()); + tmpComposer.setRenderer(LookUp.IsVisible, new BooleanCellRenderer()); + tmpComposer.setRenderer(LookUp.Index, new PrePendRenderer("Trk: ")); + tmpComposer.setRenderer(LookUp.HSBL, new ColorHSBLCellRenderer(false)); + +// KeyType[] sortKeyArr = {KeyType.Hue, KeyType.Saturation, KeyType.Luminance}; + List sortKeyL_HBL = ImmutableList.of(KeyAttr.Hue, KeyAttr.Brightness, KeyAttr.Luminance); + List sortKeyL_HLB = ImmutableList.of(KeyAttr.Hue, KeyAttr.Luminance, KeyAttr.Brightness); + List sortKeyL_HLS = ImmutableList.of(KeyAttr.Hue, KeyAttr.Luminance, KeyAttr.Saturation); + List sortKeyL_HSB = ImmutableList.of(KeyAttr.Hue, KeyAttr.Saturation, KeyAttr.Brightness); + List sortKeyL_HSL = ImmutableList.of(KeyAttr.Hue, KeyAttr.Saturation, KeyAttr.Luminance); + List sortKeyL_SBH = ImmutableList.of(KeyAttr.Saturation, KeyAttr.Brightness, KeyAttr.Hue); + List sortKeyL_SBL = ImmutableList.of(KeyAttr.Saturation, KeyAttr.Brightness, KeyAttr.Luminance); + List sortKeyL_SLB = ImmutableList.of(KeyAttr.Saturation, KeyAttr.Luminance, KeyAttr.Brightness); + + ItemHandler tmpIH = new TestItemHandler(aNumGroups); + StaticItemProcessor tmpIP = new StaticItemProcessor<>(formTestItemList(aNumItems)); + tmpILP = new ItemListPanel<>(tmpIH, tmpIP, tmpComposer, true); + tmpILP.setSortingEnabled(true); +// tmpILP.setSortComparator(ColorHSBL.class, new ColorStepSortCompartor(numGroups)); + tmpILP.setSortComparator(2, new ColorHSBLCompartor(sortKeyL_HBL, aNumGroups)); + tmpILP.setSortComparator(3, new ColorHSBLCompartor(sortKeyL_HLB, aNumGroups)); + tmpILP.setSortComparator(4, new ColorHSBLCompartor(sortKeyL_HLS, aNumGroups)); + tmpILP.setSortComparator(5, new ColorHSBLCompartor(sortKeyL_HSB, aNumGroups)); + tmpILP.setSortComparator(6, new ColorHSBLCompartor(sortKeyL_HSL, aNumGroups)); + tmpILP.setSortComparator(7, new ColorHSBLCompartor(sortKeyL_SBH, aNumGroups)); + tmpILP.setSortComparator(8, new ColorHSBLCompartor(sortKeyL_SBL, aNumGroups)); + tmpILP.setSortComparator(9, new ColorHSBLCompartor(sortKeyL_SLB, aNumGroups)); + +// JTable tmpTable = lidarILP.getTable(); +// tmpTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); +// retPanel.add(new JScrollPane(tmpTable), "growx,growy,pushx,pushy,span,wrap"); + retPanel.add(tmpILP, "growx,growy,pushx,pushy,span,wrap"); + + tmpIP.setItems(formTestItemList(aNumItems)); + + return retPanel; + } + + /** + * Helper method that forms the list of TestItems. + */ + private static List formTestItemList(int aNumItems) + { + List retL = new ArrayList<>(); + + Random tmpRandom = new Random(100); + for (int c1 = 0; c1 < aNumItems; c1++) + { + int r = tmpRandom.nextInt(256); + int g = tmpRandom.nextInt(256); + int b = tmpRandom.nextInt(256); + Color tmpColor = new Color(r, g, b); + String tmpMsg = String.format("Color: %3d %3d %3d", r, g, b); + retL.add(new TestItem(c1, tmpColor, tmpMsg)); + } + + return retL; + } + + enum LookUp + { + IsVisible, + + Index, + + Color, + + HSBL, + + Message, + + Red, + + Green, + + Blue, + + Hue, + + Sat, + + Lum, + + Bri + } + + static class TestItem + { + // Attributes + private final int index; + private final Color color; + private final String message; + + // Cache vars + private final ColorHSBL cHSBL; + + // State vars + private boolean isVisible; + + public TestItem(int aIndex, Color aColor, String aMessage) + { + index = aIndex; + color = aColor; + message = aMessage; + + cHSBL = new ColorHSBL(color); + + isVisible = true; + } + } + + static class TestItemHandler implements ItemHandler + { + // Attributes + private final int numGroups; + + /** Standard Constructor */ + public TestItemHandler(int aNumGroups) + { + numGroups = aNumGroups; + } + + @Override + public Object getValue(TestItem aItem, LookUp aEnum) + { + switch (aEnum) + { + case IsVisible: + return aItem.isVisible; + case Index: + return aItem.index; + case Color: + return aItem.color; + case HSBL: + return aItem.cHSBL; + case Message: + return aItem.message; + case Red: + return aItem.color.getRed(); + case Green: + return aItem.color.getGreen(); + case Blue: + return aItem.color.getBlue(); + case Hue: + return (int) (aItem.cHSBL.getHue() * numGroups); + case Sat: + return (int) (aItem.cHSBL.getSaturation() * numGroups); + case Bri: + return (int) (aItem.cHSBL.getBrightness() * numGroups); + case Lum: + return (int) (aItem.cHSBL.getLuminance() * numGroups); + default: + break; + } + + throw new UnsupportedOperationException("Column is not supported. Enum: " + aEnum); + } + + @Override + public void setValue(TestItem aItem, LookUp aEnum, Object aValue) + { + if (aEnum == LookUp.IsVisible) + aItem.isVisible = (Boolean) aValue; + else + throw new UnsupportedOperationException("Column is not supported. Enum: " + aEnum); + } + + } + +} diff --git a/src/glum/color/KeyAttr.java b/src/glum/color/KeyAttr.java new file mode 100644 index 0000000..44c2312 --- /dev/null +++ b/src/glum/color/KeyAttr.java @@ -0,0 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.color; + +/** + * Enum that defines the different attributes associated with Colors. + * + * @author lopeznr1 + */ +public enum KeyAttr +{ + Hue, + + Saturation, + + Brightness, + + Luminance, + +} \ No newline at end of file diff --git a/src/glum/coord/Convert.java b/src/glum/coord/Convert.java deleted file mode 100644 index ad5af9b..0000000 --- a/src/glum/coord/Convert.java +++ /dev/null @@ -1,95 +0,0 @@ -package glum.coord; - -/** - * Contains conversion multipliers to/from feet, yards, meters, data miles, and nautical miles, as well as angular - * values to/from degrees and radians. To convert a value X in units of U to units of - * V, use X * Convert.U_TO_V. - */ -public class Convert -{ - public static final double FEET_TO_METERS = 0.3048; - public static final double DM_TO_METERS = 1828.8; - public static final double NM_TO_METERS = 1852.0; - public static final double MILES_TO_METERS = 1609.344; - public static final double YARDS_TO_METERS = 0.9144; // 3 * FEET_TO_METERS - - public static final double METERS_TO_FEET = 1.0 / FEET_TO_METERS; - public static final double DM_TO_FEET = 6000.0; - public static final double NM_TO_FEET = NM_TO_METERS * METERS_TO_FEET; - public static final double MILES_TO_FEET = 5280.0; - public static final double YARDS_TO_FEET = 3.0; - - public static final double METERS_TO_DM = 1.0 / DM_TO_METERS; - public static final double FEET_TO_DM = FEET_TO_METERS * METERS_TO_DM; - public static final double NM_TO_DM = NM_TO_METERS * METERS_TO_DM; - public static final double MILES_TO_DM = MILES_TO_METERS * METERS_TO_DM; - public static final double YARDS_TO_DM = YARDS_TO_METERS * METERS_TO_DM; - - public static final double METERS_TO_NM = 1.0 / NM_TO_METERS; - public static final double FEET_TO_NM = FEET_TO_METERS * METERS_TO_NM; - public static final double DM_TO_NM = DM_TO_METERS * METERS_TO_NM; - public static final double MILES_TO_NM = MILES_TO_METERS * METERS_TO_NM; - public static final double YARDS_TO_NM = YARDS_TO_METERS * NM_TO_METERS; - - public static final double METERS_TO_MILES = 1.0 / MILES_TO_METERS; - public static final double FEET_TO_MILES = FEET_TO_METERS * METERS_TO_MILES; - public static final double DM_TO_MILES = DM_TO_METERS * METERS_TO_MILES; - public static final double NM_TO_MILES = NM_TO_METERS * METERS_TO_MILES; - public static final double YARDS_TO_MILES = YARDS_TO_METERS * METERS_TO_MILES; - - public static final double METERS_TO_YARDS = 1.0 / YARDS_TO_METERS; - public static final double FEET_TO_YARDS = 1.0 / 3.0; - public static final double DM_TO_YARDS = 2000.0; - public static final double NM_TO_YARDS = NM_TO_METERS * METERS_TO_YARDS; - public static final double MILES_TO_YARDS = 1760.0; - - public static final double RAD_TO_DEG = 180.0 / Math.PI; - public static final double DEG_TO_RAD = Math.PI / 180.0; - - public static final double SECS_TO_MSECS = 1000.0; - public static final double MSECS_TO_SECS = 1.0 / SECS_TO_MSECS; - - public static final int MINS_TO_SECS = 60; - public static final int HOURS_TO_MINS = 60; - public static final int HOURS_TO_SECS = HOURS_TO_MINS * MINS_TO_SECS; - - public static final double SECS_TO_MINS = 1 / MINS_TO_SECS; - public static final double MINS_TO_HOURS = 1 / HOURS_TO_MINS; - public static final double SECS_TO_HOURS = 1 / HOURS_TO_SECS; - - /** - * Constructor - */ - private Convert() - { - } - - /** - * Converts an angle to a bearing - */ - public static double angleToBearing(double aAngle) - { - double bearing; - - bearing = 180 - (aAngle + 90); - if (bearing < 0) - bearing += 360; - - return bearing; - } - - /** - * Converts a bearing to an angle - */ - public static double bearingToAngle(double aBearing) - { - double angle; - - angle = 180 - (aBearing + 90); - if (angle < 0) - angle += 360; - - return angle; - } - -} diff --git a/src/glum/coord/CoordUtil.java b/src/glum/coord/CoordUtil.java deleted file mode 100644 index c0fa1a6..0000000 --- a/src/glum/coord/CoordUtil.java +++ /dev/null @@ -1,197 +0,0 @@ -package glum.coord; - - -/** Provides a few useful functions on coordinates, such as converting -* to a user-presentable string. -*/ -public class CoordUtil -{ - /** Convert a Lat/Lon to a pair of DEG:MM:SS H strings. H is the - * hemisphere, N or S for lat, E or W for lon. The default separator - * string LL_SEP is used. - */ - public static String LatLonToString (LatLon ll) - { - return ll == null ? "" : LatLonToString (ll, LL_SEP); - } - - /** Same as the other LatLonToString, excepts this one uses the - * given sep string to separate the Lat and Lon. - */ - public static String LatLonToString (LatLon ll, String sep) - { - return ll == null ? "" : - LatToString (ll.lat) + LL_SEP + LonToString (ll.lon); - } - - /** Converts the given lat to DD:MM:SS H. */ - - public static String LatToString (double lat) - { - return LatToString (lat, true); - } - - /** Converts the given lat to DD:MM:SS H if - * include_seconds is true. If it's false, then the - * :SS part is left off. - */ - - public static String LatToString (double lat, boolean include_seconds) - { - DMS dms = new DMS (lat); - StringBuffer s = new StringBuffer(); - - if ( dms.degrees < 10 ) - s.append ("0"); - s.append (dms.degrees); - s.append (":"); - if ( dms.minutes < 10 ) - s.append ("0"); - s.append (dms.minutes); - if ( include_seconds ) - { - s.append (":"); - if ( dms.seconds < 10 ) - s.append ("0"); - s.append (dms.seconds); - } - s.append (lat >= 0 ? " N" : " S"); - return s.toString(); - } - - /** Similar to LatToString except that the degrees - * part is DDD instead of DD. */ - - public static String LonToString (double lon) - { - return LonToString (lon, true); - } - - /** Similar to LatToString except that the degrees - * part is DDD instead of DD. */ - - public static String LonToString (double lon, boolean include_seconds) - { - DMS dms = new DMS (lon); - StringBuffer s = new StringBuffer(); - - if ( dms.degrees < 100 ) - s.append ("0"); - if ( dms.degrees < 10 ) - s.append ("0"); - s.append (dms.degrees); - s.append (":"); - if ( dms.minutes < 10 ) - s.append ("0"); - s.append (dms.minutes); - if ( include_seconds ) - { - s.append (":"); - if ( dms.seconds < 10 ) - s.append ("0"); - s.append (dms.seconds); - } - s.append (lon >= 0 ? " E" : " W"); - return s.toString(); - } - - /** Converts dmsh_string to a double value. - * The string format should match the output of the - * LatToString formats, including hemisphere. - * If a hemisphere character is not part of the string, the - * returned value will be non-negative. - */ - public static double StringToLat (String dmsh_string) - { - if ( dmsh_string == null || dmsh_string.length() == 0 ) - return 0.0; - - int dms [] = StringToDMS (dmsh_string); - - if ( dms.length == 3 ) - return new Degrees (dms[0], dms[1], dms[2]).degrees; - else return 0.0; - } - - /** {@see StringToLat} */ - - public static double StringToLon (String dmsh_string) - { - // Because we aren't doing any range or hemisphere error - // checking, a lon value is identical to a lat value. - return StringToLat (dmsh_string); - } - - /** Converts dmsh_string to a an array of - * 3 ints representing degrees, minutes, and seconds. - * if a hemisphere character is present (one of NSEW or - * nsew), and it represents a souther or western hemisphere, - * then the degrees value, in index 0 of the returned array, - * will be a non-positive number. - */ - public static int [] StringToDMS (String dmsh_string) - { - if ( dmsh_string == null || dmsh_string.length() == 0 ) - return null; - - char chars [] = dmsh_string.toCharArray(); - int dms [] = new int [ 3 ]; - - dms[0] = 0; - for ( int i = 0, j = 0; i < chars.length; i++ ) - { - char c = chars[i]; - - if ( c == ' ' || c == ' ' ) // Space or tab. - continue; - else if ( c >= '0' && c <= '9' && j < 3 ) - dms[j] = dms[j] * 10 + c - '0'; - else if ( c == ':' ) - { - j++; - dms[j] = 0; - } - else if ( c == 'S' || c == 's' || c == 'W' || c == 'w' ) - dms[0] = -dms[0]; - } - - return dms; - } - - public static class DMS - { - public DMS (double deg) - { - if ( deg < 0 ) deg = -deg; - degrees = (int) deg; - minutes = (int) (deg * 60) % 60; - seconds = (int) (deg * 3600) % 60; - } - public int degrees, minutes, seconds; - } - - public static class Degrees - { - public Degrees (int deg, int min, int sec) - { - degrees = Math.abs (deg) + - Math.abs(min) / 60.0 + - Math.abs(sec) / 3600.0; - if ( deg < 0 || min < 0 || sec < 0 ) - degrees = -degrees; - } - public Degrees (int deg, int min, int sec, char hemisphere) - { - this (deg, min, sec); - if ( hemisphere == 'N' || hemisphere == 'n' || - hemisphere == 'E' || hemisphere == 'e' ) - degrees = Math.abs (degrees); - else if ( hemisphere == 'S' || hemisphere == 's' || - hemisphere == 'W' || hemisphere == 'w' ) - degrees = -Math.abs (degrees); - } - public double degrees; - } - - public static String LL_SEP = " / "; -} diff --git a/src/glum/coord/Epsilon.java b/src/glum/coord/Epsilon.java deleted file mode 100644 index 3c4012a..0000000 --- a/src/glum/coord/Epsilon.java +++ /dev/null @@ -1,43 +0,0 @@ -package glum.coord; - -/** Determines if two numbers are close, usually as a way to say -* that they are equal. Close is defined to mean that their difference -* is less than some small number, which is either supplied by the caller -* or is EPSILON. -*

For longitude near the equator, a difference of EPSILON is about -* 3.65 feet (where the earth's circumference is about 21913.3 DM, or -* about 60.87 DM per degree longitude). For DataMile measurements, it's -* about 0.72 inches. -*/ - -public class Epsilon -{ - /** The measure of closeness; set to 0.00001. */ - public static final double EPSILON = 0.00001; - - public static boolean close (float a, float b) - { - float diff = a - b; - return diff < EPSILON && diff > -EPSILON; - } - - public static boolean close (float a, float b, float epsilon) - { - float diff = a - b; - return diff < epsilon && diff > -epsilon; - } - - public static boolean close (double a, double b) - { - double diff = a - b; - return diff < EPSILON && diff > -EPSILON; - } - - public static boolean close (double a, double b, float epsilon) - { - double diff = a - b; - return diff < EPSILON && diff > -EPSILON; - } - - private Epsilon () { } -} diff --git a/src/glum/coord/GeoUtil.java b/src/glum/coord/GeoUtil.java deleted file mode 100644 index 4f76522..0000000 --- a/src/glum/coord/GeoUtil.java +++ /dev/null @@ -1,97 +0,0 @@ -package glum.coord; - -/** - * Contains a collection of utility methods to perform linear algebra using the objects from this package. - */ -public class GeoUtil -{ - /** - * realSqr returns aNum*aNum - */ - public static double realSqr(double aNum) - { - return aNum * aNum; - } - - /** - * computeDotProduct - Returns the dot product of vector1 and vector2 - */ - public static double computeDotProduct(Point3D vector1, Point3D vector2) - { - return vector1.x * vector2.x + vector1.y * vector2.y + vector1.z * vector2.z; - } - - /** - * computeDistance - Returns the distance between pt1 and pt2 - */ - public static double computeDistance(Point3D pt1, Point3D pt2) - { - return Math.sqrt(realSqr(pt1.x - pt2.x) + realSqr(pt1.y - pt2.y) + realSqr(pt1.z - pt2.z)); - } - - /** - * computeDistanceSquare - Returns the squared distance between pt1 and pt2 - */ - public static double computeDistanceSquare(Point3D pt1, Point3D pt2) - { - return realSqr(pt1.x - pt2.x) + realSqr(pt1.y - pt2.y) + realSqr(pt1.z - pt2.z); - } - - /** - * computeLength - Returns the magnitude of aVector - */ - public static double computeLength(Point3D aVector) - { - return Math.sqrt(realSqr(aVector.x) + realSqr(aVector.y) + realSqr(aVector.z)); - } - - /** - * computeNormal - Returns the R.H.R normal defined by the 3 points - */ - public static void computeNormal(Point3D pt1, Point3D pt2, Point3D pt3, Point3D aNormal) - { - Point3D vector1, vector2; - - vector1 = new Point3D(); - vector2 = new Point3D(); - computeVector(pt1, pt3, vector1); - computeVector(pt3, pt2, vector2); - - // ! Not sure why I have to negate all the values; Need to refer to linear alg. -//! aNormal.x = vector1.y*vector2.z - vector1.z*vector2.y; -//! aNormal.y = vector1.z*vector2.x - vector1.x*vector2.z; -//! aNormal.z = vector1.x*vector2.y - vector1.y*vector2.x; - aNormal.x = -(vector1.y * vector2.z - vector1.z * vector2.y); - aNormal.y = -(vector1.z * vector2.x - vector1.x * vector2.z); - aNormal.z = -(vector1.x * vector2.y - vector1.y * vector2.x); - - // Normalize the vector - normalizeVector(aNormal); - } - - /** - * computeVector - Returns the vector defined by the 2 points - */ - public static void computeVector(Point3D pt1, Point3D pt2, Point3D aVector) - { - aVector.x = pt2.x - pt1.x; - aVector.y = pt2.y - pt1.y; - aVector.z = pt2.z - pt1.z; - } - - /** - * normalizeVector - Normalizes aVector so that its length is 1 - */ - public static void normalizeVector(Point3D aVector) - { - double length; - - length = computeLength(aVector); - - // Normalize the vector - aVector.x = aVector.x / length; - aVector.y = aVector.y / length; - aVector.z = aVector.z / length; - } - -} diff --git a/src/glum/coord/LatLon.java b/src/glum/coord/LatLon.java deleted file mode 100644 index c9a954c..0000000 --- a/src/glum/coord/LatLon.java +++ /dev/null @@ -1,111 +0,0 @@ -package glum.coord; - -/** Simple class for Lat/Lon values. */ -public class LatLon -{ - public double lat; - public double lon; - - public LatLon() - { - } - - public LatLon(LatLon latlon) - { - if (latlon != null) - { - lat = latlon.lat; - lon = latlon.lon; - } - } - - public LatLon(double lat, double lon) - { - this.lat = lat; - this.lon = lon; - } - - public LatLon(String lat_string, String lon_string) - { - set(lat_string, lon_string); - } - - public void set(double lat, double lon) - { - this.lat = lat; - this.lon = lon; - } - - public void set(LatLon latlon) - { - if (latlon != null) - { - lat = latlon.lat; - lon = latlon.lon; - } - } - - public void set(String lat_string, String lon_string) - { - lat = CoordUtil.StringToLat(lat_string); - lon = CoordUtil.StringToLon(lon_string); - } - - public void normalize() - { - if (lat > 90) - lat = 90; - else if (lat < -90) - lat = -90; - - if (lon > 180) - lon -= 360; - else if (lon < -180) - lon += 360; - } - - /** - * Tests to see if the given object is the same lat/lon as this position. - * "Same" really means "very, very close," as defined by {@link Epsilon}. - * - * @return True if obj is a LatLon and is very close to our lat/lon position. - * False otherwise. - */ - @Override - public boolean equals(Object obj) - { - return (obj instanceof LatLon) && Epsilon.close(lat, ((LatLon)obj).lat) && Epsilon.close(lon, ((LatLon)obj).lon); - } - - @Override - public String toString() - { - return CoordUtil.LatLonToString(this); - } - - /** - * Returns the change in latitude - */ - static public double computeDeltaLat(double lat1, double lat2) - { - return lat2 - lat1; - } - - /** - * Returns the change in longitude - */ - static public double computeDeltaLon(double lon1, double lon2) - { - double dLon; - - dLon = lon2 - lon1; - if (Math.abs(dLon) < 180) - return dLon; - - if (dLon > 180) - return dLon - 360; - else - return dLon + 360; - } - -} diff --git a/src/glum/coord/Point2D.java b/src/glum/coord/Point2D.java deleted file mode 100644 index 7bff1fe..0000000 --- a/src/glum/coord/Point2D.java +++ /dev/null @@ -1,37 +0,0 @@ -package glum.coord; - -public class Point2D -{ - public double x; - public double y; - - public Point2D () { } - - public Point2D (Point2D pt) - { if ( pt != null ) { x = pt.x; y = pt.y; } } - - public Point2D (double x, double y) - { this.x = x; this.y = y; } - - public void set (double x, double y) - { this.x = x; this.y = y; } - - public void set (Point2D pt) - { if ( pt != null ) { x = pt.x; y = pt.y; } } - - public double distance (Point2D aPt) - { - if (aPt == null) - return 0; - - return Math.sqrt((aPt.x - x)*(aPt.x - x) + (aPt.y - y)*(aPt.y - y)); - } - - @Override - public boolean equals (Object obj) - { - return (obj instanceof Point2D) && - Epsilon.close (x, ((Point2D) obj).x) && - Epsilon.close (y, ((Point2D) obj).y); - } -} diff --git a/src/glum/coord/Point2Di.java b/src/glum/coord/Point2Di.java deleted file mode 100644 index f7f09d5..0000000 --- a/src/glum/coord/Point2Di.java +++ /dev/null @@ -1,27 +0,0 @@ -package glum.coord; - -public class Point2Di -{ - public int x; - public int y; - - public Point2Di () { } - - public Point2Di (Point2Di pt) - { if ( pt != null ) { x = pt.x; y = pt.y; } } - - public Point2Di (int x, int y) { this.x = x; this.y = y; } - - public void set (int x, int y) { this.x = x; this.y = y; } - - public void set (Point2Di pt) - { if ( pt != null ) { x = pt.x; y = pt.y; } } - - @Override - public boolean equals (Object obj) - { - return (obj instanceof Point2Di) && - x == ((Point2Di) obj).x && - y == ((Point2Di) obj).y; - } -} diff --git a/src/glum/coord/Point3D.java b/src/glum/coord/Point3D.java deleted file mode 100644 index 7fabc60..0000000 --- a/src/glum/coord/Point3D.java +++ /dev/null @@ -1,42 +0,0 @@ -package glum.coord; - -/** A class for representing any 3-Dimensional vector, which could be -* a position, a velocity, or a rotation. No information about units -* is assumed or implied. -*/ - -public class Point3D -{ - public double x; - public double y; - public double z; - - public Point3D () { } - - public Point3D (Point3D pt) - { if ( pt != null ) { x = pt.x; y = pt.y; z = pt.z; } } - - public Point3D (double x, double y, double z) - { this.x = x; this.y = y; this.z = z; } - - public void set (double x, double y, double z) - { this.x = x; this.y = y; this.z = z; } - - public void set (Point3D pt) - { if ( pt != null ) { x = pt.x; y = pt.y; z = pt.z; } } - - @Override - public boolean equals (Object obj) - { - return (obj instanceof Point3D) && - Epsilon.close (x, ((Point3D) obj).x) && - Epsilon.close (y, ((Point3D) obj).y) && - Epsilon.close (z, ((Point3D) obj).z); - } - - @Override - public String toString() - { - return new String("(" + x + ", " + y + ", " + z + ")"); - } -} diff --git a/src/glum/coord/RngBrg.java b/src/glum/coord/RngBrg.java deleted file mode 100644 index cc79fe2..0000000 --- a/src/glum/coord/RngBrg.java +++ /dev/null @@ -1,47 +0,0 @@ -package glum.coord; - -public class RngBrg -{ - public double rng; - public double brg; - - public RngBrg() { } - - public RngBrg(RngBrg pt) - { - if ( pt != null ) - { - rng = pt.rng; - brg = pt.brg; - } - } - - public RngBrg (double rng, double brg) - { - this.rng = rng; - this.brg = brg; - } - - public void set (double rng, double brg) - { - this.rng = rng; - this.brg = brg; - } - - public void set (RngBrg pt) - { - if ( pt != null ) - { - rng = pt.rng; - brg = pt.brg; - } - } - - @Override - public boolean equals (Object obj) - { - return (obj instanceof RngBrg) && - Epsilon.close (rng, ((RngBrg) obj).rng) && - Epsilon.close (brg, ((RngBrg) obj).brg); - } -} diff --git a/src/glum/coord/UV.java b/src/glum/coord/UV.java deleted file mode 100644 index 8c7150d..0000000 --- a/src/glum/coord/UV.java +++ /dev/null @@ -1,26 +0,0 @@ -package glum.coord; - -public class UV extends Point2D -{ - public UV () - { - x = 0; - y = 0; - } - - public UV (Point2D pt) - { - if (pt != null) - { - x = pt.x; - y = pt.y; - } - } - - public UV (double x, double y) - { - this.x = x; - this.y = y; - } - -} diff --git a/src/glum/database/QueryItem.java b/src/glum/database/QueryItem.java index 6a8aa95..719d4f6 100644 --- a/src/glum/database/QueryItem.java +++ b/src/glum/database/QueryItem.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.database; public abstract interface QueryItem> diff --git a/src/glum/database/QueryItemComparator.java b/src/glum/database/QueryItemComparator.java index 3365316..58a0e1c 100644 --- a/src/glum/database/QueryItemComparator.java +++ b/src/glum/database/QueryItemComparator.java @@ -1,11 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.database; import java.util.Comparator; +/** + * {@link Comparable} to sort {@link QueryItem}s based on the provided key. + * + * @author lopeznr1 + */ public class QueryItemComparator, G2 extends Enum> implements Comparator { - private G2 sortKey; + // Attributes + private final G2 sortKey; + /** Standard Constructor */ public QueryItemComparator(G2 aSortKey) { sortKey = aSortKey; @@ -15,10 +35,8 @@ public class QueryItemComparator, G2 extends Enum> i @Override public int compare(G1 item1, G1 item2) { - Comparable value1, value2; - - value1 = (Comparable)item1.getValue(sortKey); - value2 = (Comparable)item2.getValue(sortKey); + var value1 = (Comparable) item1.getValue(sortKey); + var value2 = (Comparable) item2.getValue(sortKey); if (value1 == null && value2 == null) return 0; @@ -34,29 +52,23 @@ public class QueryItemComparator, G2 extends Enum> i /** * Utility method to create a QueryItemComparator by specifying the class and sort Enum. - *

+ *

* This logic is here due to Java's horrible implementation off generics. */ public static , G4 extends Enum> Comparator spawn(Class aClass, G4 aEnum) { - QueryItemComparator retComparator; - - retComparator = new QueryItemComparator(aEnum); - return retComparator; + return new QueryItemComparator(aEnum); } /** * Utility method to create a QueryItemComparator by specifying just the Enum. Note this method can not be used in a * argument to another method; instead use: {@link #spawn(Class, Enum)} - *

+ *

* This logic is here due to Java's horrible implementation off generics. */ public static , G4 extends Enum> Comparator spawn(G4 aEnum) { - QueryItemComparator retComparator; - - retComparator = new QueryItemComparator(aEnum); - return retComparator; + return new QueryItemComparator(aEnum); } } diff --git a/src/glum/digest/Digest.java b/src/glum/digest/Digest.java new file mode 100644 index 0000000..34c0953 --- /dev/null +++ b/src/glum/digest/Digest.java @@ -0,0 +1,101 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.digest; + +import java.util.Arrays; + +/** + * Immutable object that stores a single digest. + * + * @author lopeznr1 + */ +public class Digest +{ + private final DigestType digestType; + private final byte[] digestValueArr; + + public Digest(DigestType aDigestType, byte[] aDigestValueArr) + { + digestType = aDigestType; + digestValueArr = Arrays.copyOf(aDigestValueArr, aDigestValueArr.length); + } + + public Digest(DigestType aDigestType, String aHexStr) + { + digestType = aDigestType; + digestValueArr = DigestUtils.hexStr2ByteArr(aHexStr); + } + + /** + * Returns a user friendly description (string) of this digest result. + *

+ * The result will be DigestType:hexDigestValue + */ + public String getDescr() + { + return "" + digestType + ":" + getValueAsString(); + } + + /** + * Returns the DigestType associated with this Digest. + */ + public DigestType getType() + { + return digestType; + } + + /** + * Returns the actual digest (as a string) associated with this Digest. + */ + public byte[] getValue() + { + return Arrays.copyOf(digestValueArr, digestValueArr.length); + } + + /** + * Returns the actual digest (as a string) associated with this Digest. + */ + public String getValueAsString() + { + return DigestUtils.byteArr2HexStr(digestValueArr); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((digestType == null) ? 0 : digestType.hashCode()); + result = prime * result + Arrays.hashCode(digestValueArr); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Digest other = (Digest) obj; + if (digestType != other.digestType) + return false; + if (!Arrays.equals(digestValueArr, other.digestValueArr)) + return false; + return true; + } + +} diff --git a/src/glum/digest/DigestType.java b/src/glum/digest/DigestType.java new file mode 100644 index 0000000..c9091d1 --- /dev/null +++ b/src/glum/digest/DigestType.java @@ -0,0 +1,64 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.digest; + +/** + * Enum that defines the supported digest types. + * + * @author lopeznr1 + */ +public enum DigestType +{ + // Weak digest - but very fast + MD5("MD5"), + + // Fairly strong digest type (with good performance on 32 bit machines) + SHA256("SHA-256"), + + // Very strong digest type + SHA512("SHA-512"); + + // State vars + private String algName; + + private DigestType(String aAlgName) + { + algName = aAlgName; + } + + /** + * Returns the official digest algorithm name. + * + * @see http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA + */ + public String getAlgName() + { + return algName; + } + + /** + * Returns the corresponding DigestType. + */ + public static DigestType parse(String aStr) + { + if (aStr.equalsIgnoreCase("MD5") == true) + return MD5; + if (aStr.equalsIgnoreCase("SHA256") == true) + return SHA256; + if (aStr.equalsIgnoreCase("SHA512") == true) + return SHA512; + + return null; + } +} \ No newline at end of file diff --git a/src/glum/digest/DigestUtils.java b/src/glum/digest/DigestUtils.java new file mode 100644 index 0000000..a35082a --- /dev/null +++ b/src/glum/digest/DigestUtils.java @@ -0,0 +1,83 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.digest; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import com.google.common.io.BaseEncoding; + +/** + * Collection of utility methods to ease working with the MessageDigest and associated classes. + * + * @author lopeznr1 + */ +public class DigestUtils +{ + /** + * Utility method that will throw a RuntimeExcepption if the specified digest function is not found. + *

+ * Algorithm should be MD5, SHA-256, SHA-512, ... + *

+ * See: http://docs.oracle.com/javase/1.8.0/docs/guide/security/CryptoSpec.html#AppA + */ + public static MessageDigest getDigest(String aAlgorithm) + { + MessageDigest retDigest; + + try + { + retDigest = MessageDigest.getInstance(aAlgorithm); + } + catch (NoSuchAlgorithmException aExp) + { + throw new RuntimeException("Digest not found. Digest algorith not found: " + aAlgorithm); + } + + return retDigest; + } + + /** + * Utility method that will throw a RuntimeExcepption if the specified digest function is not found. + *

+ * See: http://docs.oracle.com/javase/1.8.0/docs/guide/security/CryptoSpec.html#AppA + */ + public static MessageDigest getDigest(DigestType aDigestType) + { + return getDigest(aDigestType.getAlgName()); + } + + /** + * Utility method that returns the (lower case) hex string corresponding to the byte array. + *

+ * Delegates to {@link BaseEncoding.base16().lowerCase().encode(CharSequence)} + */ + public static String byteArr2HexStr(byte[] aByteArr) + { + String retStr = BaseEncoding.base16().lowerCase().encode(aByteArr); + return retStr; + } + + /** + * Utility method that returns a byte array corresponding to the hex string. + *

+ * Delegates to {@link BaseEncoding.base16().lowerCase().decode(CharSequence)} + */ + public static byte[] hexStr2ByteArr(String aHexStr) + { + byte[] retArr = BaseEncoding.base16().lowerCase().decode(aHexStr); + return retArr; + } + +} diff --git a/src/glum/filter/EnumFilter.java b/src/glum/filter/EnumFilter.java index 2782748..e852807 100644 --- a/src/glum/filter/EnumFilter.java +++ b/src/glum/filter/EnumFilter.java @@ -1,35 +1,50 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.filter; +import java.io.IOException; +import java.util.*; + import glum.zio.*; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - +/** + * A {@link Filter} which performs filtering based on a enam values. + * + * @author lopeznr1 + * + * @param + */ public abstract class EnumFilter> implements ZioObj, Filter { // Static config vars private Map> fullMap; - + // State vars - private Set> validSet; + private Set> validS; private boolean isEnabled; - + + /** Standard Constructor */ public EnumFilter(Class> enumClass) { - fullMap = Maps.newLinkedHashMap(); + fullMap = new LinkedHashMap<>(); for (Enum aEnum : enumClass.getEnumConstants()) fullMap.put(aEnum.ordinal(), aEnum); - - validSet = Sets.newLinkedHashSet(); + + validS = new LinkedHashSet<>(); isEnabled = false; } - + /** * Returns true if the filter is active. */ @@ -43,7 +58,7 @@ public abstract class EnumFilter> implements ZioObj, Filt */ public List> getSelectedItems() { - return Lists.newArrayList(validSet); + return new ArrayList<>(validS); } /** @@ -51,10 +66,10 @@ public abstract class EnumFilter> implements ZioObj, Filt */ public void set(EnumFilter aFilter) { - validSet = Sets.newLinkedHashSet(aFilter.validSet); + validS = new LinkedHashSet<>(aFilter.validS); isEnabled = aFilter.getIsEnabled(); } - + /** * Sets whether the filter is active. */ @@ -62,57 +77,52 @@ public abstract class EnumFilter> implements ZioObj, Filt { isEnabled = aBool; } - + /** * Sets the list of valid enums for this filter. */ - public void setSetSelectedItems(List> selectedItems) + public void setSetSelectedItems(List> aItemL) { - validSet.clear(); - validSet.addAll(selectedItems); + validS.clear(); + validS.addAll(aItemL); } - + @Override public void zioRead(ZinStream aStream) throws IOException { - int numItems; - aStream.readVersion(0); - + // Read the payload isEnabled = aStream.readBool(); - - validSet.clear(); - numItems = aStream.readInt(); + + validS.clear(); + var numItems = aStream.readInt(); for (int c1 = 0; c1 < numItems; c1++) - validSet.add(fullMap.get(aStream.readInt())); + validS.add(fullMap.get(aStream.readInt())); } @Override public void zioWrite(ZoutStream aStream) throws IOException { - int numItems; - aStream.writeVersion(0); - + aStream.writeBool(isEnabled); - numItems = validSet.size(); + var numItems = validS.size(); aStream.writeInt(numItems); - for (Enum aEnum : validSet) + for (Enum aEnum : validS) aStream.writeInt(aEnum.ordinal()); } /** - * Utility method that returns whether aValue is within the constraints - * specified by this filter. + * Utility method that returns whether aValue is within the constraints specified by this filter. */ protected boolean testIsValid(G2 aEnum) { if (isEnabled == false) return true; - - return validSet.contains(aEnum); + + return validS.contains(aEnum); } } diff --git a/src/glum/filter/Filter.java b/src/glum/filter/Filter.java index dd1cd69..9761fd3 100644 --- a/src/glum/filter/Filter.java +++ b/src/glum/filter/Filter.java @@ -1,7 +1,27 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.filter; import glum.zio.ZioObj; +/** + * Interface that provides a mechanism for filtering items. + * + * @author lopeznr1 + * + * @param + */ public interface Filter extends ZioObj { /** diff --git a/src/glum/filter/FilterUtil.java b/src/glum/filter/FilterUtil.java index f5b5bb9..65d9c53 100644 --- a/src/glum/filter/FilterUtil.java +++ b/src/glum/filter/FilterUtil.java @@ -1,14 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.filter; +import java.util.ArrayList; import java.util.List; import javax.swing.JCheckBox; -import com.google.common.collect.Lists; - import glum.gui.component.GList; import glum.gui.component.GNumberField; +/** + * Collection of utility methods used to aide with working with {@link Filter}s and (swing) UI components. + * + * @author lopeznr1 + */ public class FilterUtil { /** @@ -16,16 +33,14 @@ public class FilterUtil */ public static List applyFilter(List itemList, Filter aFilter) { - List retList; - - retList = Lists.newArrayList(); + var retItemL = new ArrayList(); for (G1 aItem : itemList) { if (aFilter.isValid(aItem) == true) - retList.add(aItem); + retItemL.add(aItem); } - return retList; + return retItemL; } /** @@ -38,18 +53,19 @@ public class FilterUtil } /** - * Utility method to synchronize the associated GUI controls with the specified filter. + * Utility method to synchronize the associated GUI controls with the specified filter. */ public static void setEnumFilter(EnumFilter> aFilter, JCheckBox mainCB, GList> mainList) { mainCB.setSelected(aFilter.getIsEnabled()); mainList.setSelectedItems(aFilter.getSelectedItems()); } - + /** * Utility method to synchronize the specified filter with the associated GUI controls. */ - public static void getRangeFilter(RangeFilter aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF) + public static void getRangeFilter(RangeFilter aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, + GNumberField minNF, GNumberField maxNF) { aFilter.setIsEnabled(mainCB.isSelected()); aFilter.setUseMin(minCB.isSelected()); @@ -59,9 +75,10 @@ public class FilterUtil } /** - * Utility method to synchronize the associated GUI controls with the specified filter. + * Utility method to synchronize the associated GUI controls with the specified filter. */ - public static void setRangeGui(RangeFilter aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF) + public static void setRangeGui(RangeFilter aFilter, JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, + GNumberField minNF, GNumberField maxNF) { mainCB.setSelected(aFilter.getIsEnabled()); minCB.setSelected(aFilter.getUseMin()); @@ -71,26 +88,23 @@ public class FilterUtil } /** - * Utility method to keep the various GUI components associated with an EnumFilter synchronized. - * The mainList will be enabled/disabled based on the selection state of mainCB. + * Utility method to keep the various GUI components associated with an EnumFilter synchronized. The mainList will be + * enabled/disabled based on the selection state of mainCB. */ public static void syncEnumGui(JCheckBox mainCB, GList> mainList) { - boolean isEnabled; - - isEnabled = mainCB.isSelected(); + var isEnabled = mainCB.isSelected(); mainList.setEnabled(isEnabled); } /** - * Utility method to keep the various GUI components associated with an RangeFilter synchronized. - * Gui components will be enabled/disabled based on the various check boxes. + * Utility method to keep the various GUI components associated with an RangeFilter synchronized. Gui components will + * be enabled/disabled based on the various check boxes. */ - public static void syncRangeGui(JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, GNumberField maxNF) + public static void syncRangeGui(JCheckBox mainCB, JCheckBox minCB, JCheckBox maxCB, GNumberField minNF, + GNumberField maxNF) { - boolean isEnabled; - - isEnabled = mainCB.isSelected(); + var isEnabled = mainCB.isSelected(); minCB.setEnabled(isEnabled); maxCB.setEnabled(isEnabled); minNF.setEnabled(isEnabled & minCB.isSelected()); diff --git a/src/glum/filter/NullFilter.java b/src/glum/filter/NullFilter.java index 35a22a5..39eaac0 100644 --- a/src/glum/filter/NullFilter.java +++ b/src/glum/filter/NullFilter.java @@ -1,12 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.filter; +import java.io.IOException; + import glum.zio.ZinStream; import glum.zio.ZoutStream; -import java.io.IOException; - /** - * A Filter which does not filter anything. Thus the method isValid() always returns true. + * A {@link Filter} which does not filter anything. Thus the method isValid() always returns true. + * + * @author lopeznr1 + * + * @param */ public class NullFilter implements Filter { diff --git a/src/glum/filter/RangeFilter.java b/src/glum/filter/RangeFilter.java index a544a9e..f0d3c6a 100644 --- a/src/glum/filter/RangeFilter.java +++ b/src/glum/filter/RangeFilter.java @@ -1,25 +1,39 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.filter; -import glum.zio.*; - import java.io.IOException; +import glum.zio.*; + /** - * Abstract filter which is used to filter a single value between the specified min/max ranges. The only code to write - * is the isValid() method and to call the appropriate Constructor. In the isValid() method, you should delegate filter - * logic to the method testIsValid() with the quantity of interest, and return the result from the method call. + * Abstract {@link Filter} which is used to filter a single value between the specified min/max ranges. The only code to + * write is the isValid() method. In the isValid() method, you should delegate filter logic to the method testIsValid() + * with the quantity of interest, and return the result from the method call. + * + * @author lopeznr1 + * + * @param */ -public abstract class RangeFilter implements ZioObj, Filter +public abstract class RangeFilter implements Filter, ZioObj { + // State vars private boolean isEnabled; private boolean useMin, useMax; private double minValue, maxValue; - /** - * @param aBinCode - * Unique identifier used during serialization. The value specified here should not collide with any other - * codes for which there is serialization. - */ + /** Standard Constructor */ public RangeFilter() { isEnabled = false; @@ -38,7 +52,7 @@ public abstract class RangeFilter implements ZioObj, Filter public boolean getUseMax() { return useMax; } public double getMinValue() { return minValue; } public double getMaxValue() { return maxValue; } - + public void setIsEnabled(boolean aBool) { isEnabled = aBool; } public void setUseMin(boolean aBool) { useMin = aBool; } public void setUseMax(boolean aBool) { useMax = aBool; } @@ -61,11 +75,9 @@ public abstract class RangeFilter implements ZioObj, Filter @Override public void zioRead(ZinStream aStream) throws IOException { - byte bSwitch; - aStream.readVersion(0); - bSwitch = aStream.readByte(); + byte bSwitch = aStream.readByte(); isEnabled = (bSwitch & 0x1) != 0; useMin = (bSwitch & 0x2) != 0; useMax = (bSwitch & 0x4) != 0; @@ -80,11 +92,9 @@ public abstract class RangeFilter implements ZioObj, Filter @Override public void zioWrite(ZoutStream aStream) throws IOException { - byte bSwitch; - aStream.writeVersion(0); - bSwitch = 0; + byte bSwitch = 0; if (isEnabled == true) bSwitch |= 0x1; if (useMin == true) diff --git a/src/glum/gui/FocusUtil.java b/src/glum/gui/FocusUtil.java index e37c1d5..e31add8 100644 --- a/src/glum/gui/FocusUtil.java +++ b/src/glum/gui/FocusUtil.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui; import javax.swing.*; diff --git a/src/glum/gui/GuiExeUtil.java b/src/glum/gui/GuiExeUtil.java new file mode 100644 index 0000000..7fc6215 --- /dev/null +++ b/src/glum/gui/GuiExeUtil.java @@ -0,0 +1,48 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui; + +import java.awt.Component; +import java.awt.event.HierarchyEvent; +import java.awt.event.HierarchyListener; + +/** + * Collection of execution utilities used to execute behavior at a specific time frame associated with the life cycle of + * AWT/Swing components. + * + * @author lopeznr1 + */ +public class GuiExeUtil +{ + /** + * Utility method to execute the Runnable once the specified Component is "showing" on the screen. + */ + public static void executeOnceWhenShowing(Component aComp, Runnable aRunnable) + { + aComp.addHierarchyListener(new HierarchyListener() { + + @Override + public void hierarchyChanged(HierarchyEvent aEvent) + { + if (aComp.isShowing() == false) + return; + + aRunnable.run(); + aComp.removeHierarchyListener(this); + } + }); + + } + +} diff --git a/src/glum/gui/GuiPaneUtil.java b/src/glum/gui/GuiPaneUtil.java new file mode 100644 index 0000000..fc4298b --- /dev/null +++ b/src/glum/gui/GuiPaneUtil.java @@ -0,0 +1,91 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui; + +import java.awt.Component; + +import javax.swing.RootPaneContainer; + +import glum.gui.panel.generic.MessagePanel; +import glum.util.ThreadUtil; + +/** + * Collection of AWT/Swing utilities for showing various message panes. + * + * @author lopeznr1 + */ +public class GuiPaneUtil +{ + /** + * Utility method to display an alert messages. + *

+ * Alert panel will have a nominal size of 750, 300. + */ + public static void showAlertMessage(Component aParent, String aTitle, String aInfoMsg) + { + // Delegate + showFailMessage(aParent, aTitle, aInfoMsg, null, 750, 300); + } + + /** + * Utility method to display failure messages. + *

+ * If a stack trace is specified (not null) then the {@link Throwable} will be displayed below aInfoMsg. + */ + public static void showFailMessage(Component aParent, String aTitle, String aInfoMsg, Throwable aExp, int aMaxW, + int aMaxH) + { + // Compute the panel size + int compW = aMaxW; + int compH = aMaxH; + RootPaneContainer tmpRPC = GuiUtil.getRootPaneContainer(aParent); + if (tmpRPC instanceof Component) + { + Component tmpComp = (Component) tmpRPC; + compW = (int) (tmpComp.getWidth() * 0.80); + compH = (int) (tmpComp.getHeight() * 0.80); + } + if (compW > aMaxW) + compW = aMaxW; + if (compH > aMaxH) + compH = aMaxH; + + // Setup the info message + String infoMsg = ""; + if (aInfoMsg != null) + infoMsg = aInfoMsg; + + if (aExp != null) + infoMsg += "\n" + ThreadUtil.getStackTraceClassic(aExp); + + MessagePanel tmpPanel = new MessagePanel(aParent, aTitle, compW, compH); + tmpPanel.setInfo(infoMsg, 0); + tmpPanel.setTabSize(2); + tmpPanel.setVisibleAsModal(); + } + + /** + * Utility method to display failure messages. + *

+ * The stack trace of the {@link Throwable} will be displayed below aInfoMsg. + *

+ * Error panel will have a nominal size of 750, 300. + */ + public static void showFailMessage(Component aParent, String aTitle, String aInfoMsg, Throwable aExp) + { + // Delegate + showFailMessage(aParent, aTitle, aInfoMsg, aExp, 750, 300); + } + +} diff --git a/src/glum/gui/GuiUtil.java b/src/glum/gui/GuiUtil.java index 35e17a9..58943fd 100644 --- a/src/glum/gui/GuiUtil.java +++ b/src/glum/gui/GuiUtil.java @@ -1,7 +1,22 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui; import java.awt.*; import java.awt.event.ActionListener; +import java.awt.event.ItemListener; +import java.awt.image.BufferedImage; import java.util.Collection; import javax.swing.*; @@ -11,28 +26,29 @@ import javax.swing.event.ChangeListener; import glum.gui.icon.IconUtil; import glum.reflect.Function; +/** + * Collection of AWT/Swing utilities. + * + * @author lopeznr1 + */ public class GuiUtil { /** * Method to examine the labels and returns the size of the largest button. */ - public static Dimension computePreferredJButtonSize(String... labels) + public static Dimension computePreferredJButtonSize(String... aLabelArr) { - Dimension tmpDim, maxDim; - JButton tmpB; - - maxDim = null; - tmpB = new JButton(""); + Dimension maxDim = null; + var tmpB = new JButton(""); // Find the label that requires the largest dimension - for (String aStr : labels) + for (String aStr : aLabelArr) { if (aStr == null) aStr = ""; tmpB.setText(aStr); - tmpDim = tmpB.getPreferredSize(); - + var tmpDim = tmpB.getPreferredSize(); if (maxDim == null || maxDim.getWidth() < tmpDim.getWidth()) maxDim = tmpDim; } @@ -45,30 +61,29 @@ public class GuiUtil */ public static JButton createJButton(String aTitle, ActionListener aActionListener) { - JButton tmpB; - - tmpB = new JButton(aTitle); + var tmpB = new JButton(aTitle); tmpB.addActionListener(aActionListener); + return tmpB; } + /** + * Creates the JRadioButton with the following attributes. + *

+ * TODO: Do not remove this method until DistMaker has been updated... + */ public static JButton createJButton(String aTitle, ActionListener aActionListener, Font aFont) { - JButton tmpB; - - tmpB = new JButton(aTitle); + var tmpB = new JButton(aTitle); tmpB.addActionListener(aActionListener); - if (aFont != null) - tmpB.setFont(aFont); + tmpB.setFont(aFont); return tmpB; } public static JButton createJButton(String aTitle, ActionListener aActionListener, Dimension aDimension) { - JButton tmpB; - - tmpB = new JButton(aTitle); + var tmpB = new JButton(aTitle); tmpB.addActionListener(aActionListener); // Force a dimension @@ -89,9 +104,7 @@ public class GuiUtil public static JButton createJButton(Icon aIcon, ActionListener aActionListener, String aToolTip) { - JButton tmpB; - - tmpB = new JButton(aIcon); + var tmpB = new JButton(aIcon); tmpB.addActionListener(aActionListener); if (aToolTip != null) @@ -110,9 +123,7 @@ public class GuiUtil public static JButton createJButtonViaResource(ActionListener aHandler, String aResourcePath, String aToolTip) { - JButton tmpB; - - tmpB = new JButton(IconUtil.loadIcon(aResourcePath)); + var tmpB = new JButton(IconUtil.loadIcon(aResourcePath)); tmpB.addActionListener(aHandler); if (aToolTip != null) @@ -126,47 +137,12 @@ public class GuiUtil */ public static JCheckBox createJCheckBox(String aTitle, ActionListener aActionListener) { - return createJCheckBox(aTitle, aActionListener, null); - } - - public static JCheckBox createJCheckBox(String aTitle, ActionListener aActionListener, Font aFont) - { - JCheckBox tmpCB; - - tmpCB = new JCheckBox(aTitle); + var tmpCB = new JCheckBox(aTitle); tmpCB.addActionListener(aActionListener); - if (aFont != null) - tmpCB.setFont(aFont); return tmpCB; } - /** - * Creates a JComboBox with the specified settings - */ - public static JComboBox createJComboBox(ActionListener aListener, Font aFont, Collection aItemL) - { - JComboBox tmpBox; - - tmpBox = new JComboBox(); - for (G1 aItem : aItemL) - tmpBox.addItem(aItem); - - if (aFont != null) - tmpBox.setFont(aFont); - - tmpBox.addActionListener(aListener); - return tmpBox; - } - -// /** -// * Creates a JComboBox with the specified settings -// */ -// public static JComboBox createJComboBox(ActionListener aListener, Object... itemArr) -// { -// return createJComboBox(aListener, null, itemArr); -// } - /** * Creates a JLabel with the specified settings */ @@ -177,10 +153,7 @@ public class GuiUtil public static JLabel createJLabel(String aTitle, int aAlignment, Font aFont) { - JLabel tmpL; - - tmpL = new JLabel(aTitle, aAlignment); - + var tmpL = new JLabel(aTitle, aAlignment); if (aFont != null) tmpL.setFont(aFont); @@ -188,48 +161,63 @@ public class GuiUtil } /** - * Creates the JRadioButton with the following attributes. + * Creates the {@link JComboBox} with the following attributes. */ - public static JRadioButton createJRadioButton(String aLabel, ActionListener aListener) + public static JComboBox createComboBox(ActionListener aListener, Collection aItemC) { - return createJRadioButton(aLabel, aListener, null); + var retBox = new JComboBox(); + for (G1 aItem : aItemC) + retBox.addItem(aItem); + + retBox.addActionListener(aListener); + + return retBox; } - public static JRadioButton createJRadioButton(String aLabel, ActionListener aListener, Font aFont) + /** + * Creates the JRadioButton with the following attributes. + */ + public static JRadioButton createJRadioButton(ItemListener aListener, String aLabel) { - JRadioButton tmpRB; + var retRB = new JRadioButton(aLabel); + retRB.addItemListener(aListener); - tmpRB = new JRadioButton(aLabel); - tmpRB.addActionListener(aListener); - if (aFont != null) - tmpRB.setFont(aFont); + return retRB; + } - return tmpRB; + /** + * Creates the JRadioButton with the following attributes. + *

+ * TODO: Do not remove this method until DistMaker has been updated... + */ + public static JRadioButton createJRadioButton(ItemListener aListener, String aLabel, Font aFont) + { + var retRB = new JRadioButton(aLabel); + retRB.addItemListener(aListener); + retRB.setFont(aFont); + + return retRB; } /** * Utility method for creating a visual thin divider - *

+ *

* Typically added to MigLayout (or like manager) with: add(aComp, "growx,h 4!,span,wrap"); */ public static JPanel createDivider() { - JPanel tmpPanel; + var retPanel = new JPanel(); + retPanel.setBorder(new BevelBorder(BevelBorder.RAISED)); - tmpPanel = new JPanel(); - tmpPanel.setBorder(new BevelBorder(BevelBorder.RAISED)); - - return tmpPanel; + return retPanel; } /** - * Creates an uneditable JTextArea with no border, non-opaque, line wrap enabled and word wrap enabled. + * Creates an uneditable JTextArea with the settings: non-opaque, line wrap enabled and word wrap enabled. */ public static JTextArea createUneditableTextArea(int rows, int cols) { - JTextArea tmpTA; - - tmpTA = new JTextArea("", rows, cols); + var tmpTA = new JTextArea("", rows, cols); tmpTA.setEditable(false); tmpTA.setOpaque(false); tmpTA.setLineWrap(true); @@ -243,9 +231,7 @@ public class GuiUtil */ public static JTextPane createUneditableTextPane() { - JTextPane tmpTP; - - tmpTP = new JTextPane(); + var tmpTP = new JTextPane(); tmpTP.setEditable(false); tmpTP.setOpaque(false); tmpTP.setContentType("text/html"); @@ -258,9 +244,7 @@ public class GuiUtil */ public static JTextField createUneditableTextField(String aTitle) { - JTextField tmpTF; - - tmpTF = new JTextField(aTitle); + var tmpTF = new JTextField(aTitle); tmpTF.setBorder(null); tmpTF.setEditable(false); tmpTF.setOpaque(false); @@ -268,134 +252,103 @@ public class GuiUtil return tmpTF; } + /** + * Utility helper method to create a JButton with the specified configuration. + * + * @param aListener + * A Listener registered with the JButton. + * @param aIcon + * The icon associated with the button + */ + public static JButton formButton(ActionListener aListener, Icon aIcon) + { + var retB = new JButton(); + retB.addActionListener(aListener); + retB.setIcon(aIcon); + return retB; + } + + /** + * Utility helper method to create a JButton with the specified configuration. + * + * @param aListener + * A Listener registered with the JButton. + * @param aImage + * The image to be used as an icon. + * @param aToolTip + * The tool tip associated with the JButton. + */ + public static JButton formButton(ActionListener aListener, BufferedImage aImage, String aToolTip) + { + var tmpIcon = new ImageIcon(aImage); + + var retB = new JButton(); + retB.setIcon(tmpIcon); + retB.addActionListener(aListener); + retB.setToolTipText(aToolTip); + + return retB; + } + + /** + * Utility helper method to create a JButton with the specified configuration. + * + * @param aListener + * A Listener registered with the JButton. + * @param aTitle + * The text title of the JButton. + */ + public static JButton formButton(ActionListener aListener, String aTitle) + { + var retB = new JButton(); + retB.addActionListener(aListener); + retB.setText(aTitle); + return retB; + } + + /** + * Utility helper method to create a JToggleButton with the specified configuration. + * + * @param aListener + * A Listener registered with the JButton. + * @param aPriIcon + * The icon to be used as the primary (unselected) icon. + * @param aSecIcon + * The icon to be used when the secondary (selected) icon. + * @param aToolTip + * The tool tip associated with the JToggleButton. + */ + public static JToggleButton formToggleButton(ActionListener aListener, Icon aPriIcon, Icon aSecIcon) + { + var retTB = new JToggleButton(aPriIcon, false); + retTB.setSelectedIcon(aSecIcon); + retTB.addActionListener(aListener); + + return retTB; + } + /** * Utility method to link a set of radio buttons together */ - public static void linkRadioButtons(JRadioButton... buttonArr) + public static ButtonGroup linkRadioButtons(JRadioButton... aButtonArr) { - ButtonGroup tmpGroup; + var retGroup = new ButtonGroup(); + for (var aItem : aButtonArr) + retGroup.add(aItem); - tmpGroup = new ButtonGroup(); - for (JRadioButton aItem : buttonArr) - tmpGroup.add(aItem); + return retGroup; } /** - * Reads a boolean from a string with out throwing a exception + * Utility method to link a set of radio buttons together */ - public static boolean readBoolean(String aStr, boolean aVal) + public static ButtonGroup linkRadioButtons(Collection aButtonC) { - if (aStr == null) - return aVal; + var retGroup = new ButtonGroup(); + for (var aItem : aButtonC) + retGroup.add(aItem); - // Special case for 1 char strings - if (aStr.length() == 1) - { - char aChar; - - aChar = aStr.charAt(0); - if (aChar == 'T' || aChar == 't' || aChar == '1') - return true; - - return false; - } - - try - { - return Boolean.valueOf(aStr).booleanValue(); - } - catch(Exception e) - { - return aVal; - } - } - - /** - * Reads a double from a string with out throwing a exception. Note aStr can have an number of separators: comma - * chars - */ - public static double readDouble(String aStr, double aVal) - { - try - { - aStr = aStr.replace(",", ""); - return Double.parseDouble(aStr); - } - catch(Exception e) - { - return aVal; - } - } - - /** - * Reads a float from a string with out throwing a exception. Note aStr can have an number of separators: comma chars - */ - public static float readFloat(String aStr, float aVal) - { - try - { - aStr = aStr.replace(",", ""); - return Float.parseFloat(aStr); - } - catch(Exception e) - { - return aVal; - } - } - - /** - * Reads an int from a string without throwing a exception Note aStr can have an number of separators: comma chars - */ - public static int readInt(String aStr, int aVal) - { - try - { - aStr = aStr.replace(",", ""); - return Integer.parseInt(aStr); - } - catch(Exception e) - { - return aVal; - } - } - - /** - * Reads a long from a string without throwing a exception Note aStr can have an number of separators: comma chars - */ - public static long readLong(String aStr, long aVal) - { - try - { - aStr = aStr.replace(",", ""); - return Long.parseLong(aStr); - } - catch(Exception e) - { - return aVal; - } - } - - /** - * Reads an int (forced to fit within a range) from a string with out throwing a exception - */ - public static int readRangeInt(String aStr, int minVal, int maxVal, int aVal) - { - int aInt; - - try - { - aInt = Integer.parseInt(aStr); - if (aInt < minVal) - aInt = minVal; - else if (aInt > maxVal) - aInt = maxVal; - - return aInt; - } - catch(Exception e) - { - return aVal; - } + return retGroup; } /** @@ -403,28 +356,27 @@ public class GuiUtil */ public static RootPaneContainer getRootPaneContainer(Component aComponent) { - Container aParent; - // Check to see if the Component is an actual RootPaneContainer if (aComponent instanceof RootPaneContainer) - return (RootPaneContainer)aComponent; + return (RootPaneContainer) aComponent; // Attempt to locate the RootPaneContainer (through our stack) - aParent = aComponent.getParent(); - while (aParent != null && (aParent instanceof RootPaneContainer) == false) - aParent = aParent.getParent(); + var retParent = aComponent.getParent(); + while (retParent != null && (retParent instanceof RootPaneContainer) == false) + retParent = retParent.getParent(); // Bail if we failed to find the RootPaneContainer - if (aParent instanceof RootPaneContainer == false) + if (retParent instanceof RootPaneContainer == false) throw new RuntimeException("No valid (grand)parent associated with GlassPane."); - return (RootPaneContainer)aParent; + return (RootPaneContainer) retParent; } /** * Utility method to locate all of the subcomponents contained in aContainer which are an instance of searchClass */ - public static void locateAllSubComponents(Container aContainer, Collection itemList, Class... searchClassArr) + public static void locateAllSubComponents(Container aContainer, Collection itemList, + Class... searchClassArr) { for (Component aComponent : aContainer.getComponents()) { @@ -438,13 +390,14 @@ public class GuiUtil } if (aComponent instanceof Container) - locateAllSubComponents((Container)aComponent, itemList, searchClassArr); + locateAllSubComponents((Container) aComponent, itemList, searchClassArr); } } /** - * Utility method to force a Component to act as modal while it is visible Source: - * http://stackoverflow.com/questions/804023/how-do-i-simulate-a-modal-dialog-from-within-an-applet + * Utility method to force a Component to act as modal while it is visible + *

+ * Source: http://stackoverflow.com/questions/804023/how-do-i-simulate-a-modal-dialog-from-within-an-applet */ public static void modalWhileVisible(Component aComponent) { @@ -452,27 +405,27 @@ public class GuiUtil if (SwingUtilities.isEventDispatchThread() == false) throw new RuntimeException("Visibility for modal components must be changed via the Event thread."); - synchronized(aComponent) + synchronized (aComponent) { try { - EventQueue theQueue = aComponent.getToolkit().getSystemEventQueue(); + var theQueue = aComponent.getToolkit().getSystemEventQueue(); while (aComponent.isVisible()) { //System.out.println("About to dispatch event... component.isVisible():" + aComponent.isVisible()); - AWTEvent event = theQueue.getNextEvent(); - Object source = event.getSource(); + var event = theQueue.getNextEvent(); + var source = event.getSource(); if (event instanceof ActiveEvent) { - ((ActiveEvent)event).dispatch(); + ((ActiveEvent) event).dispatch(); } else if (source instanceof Component) { - ((Component)source).dispatchEvent(event); + ((Component) source).dispatchEvent(event); } else if (source instanceof MenuComponent) { - ((MenuComponent)source).dispatchEvent(event); + ((MenuComponent) source).dispatchEvent(event); } else { @@ -480,7 +433,7 @@ public class GuiUtil } } } - catch(InterruptedException ignored) + catch (InterruptedException ignored) { } } @@ -489,18 +442,16 @@ public class GuiUtil /** * Utility to call a specific method (methodName) with specific parameters (aParamArr) on aComp and on all of the * child subcomponents. The method will only be called if the components are an instance of refMatchClass. - *

- * This is useful so that a component and all of its children can be disabled, hidden, etc
+ *

+ * This is useful so that a component and all of its children can be disabled, hidden, etc
* Example: GuiUtil.callMethod(myPanel, setEnabled, false); - *

+ *

* Be aware, this is rather expensive, so do not call in time critical applications. */ public static void callMethod(Component aComp, Class refMatchClass, String aMethodName, Object... aParamArr) { - Class[] typeArr; - // Construct the associated type array - typeArr = new Class[0]; + var typeArr = new Class[0]; if (aParamArr.length > 0) { // Determine the types of the specified arguments @@ -520,24 +471,22 @@ public class GuiUtil /** * Helper method to callMethod */ - private static void callMethodHelper(Component aComp, Class refMatchClass, String aMethodName, Class[] aTypeArr, Object[] aParamArr) + private static void callMethodHelper(Component aComp, Class refMatchClass, String aMethodName, + Class[] aTypeArr, Object[] aParamArr) { - Component[] subCompArr; - Function aFunction; - // Locate and call the actual method if (refMatchClass.isInstance(aComp) == true) { try { - aFunction = new Function(aComp, aMethodName, aTypeArr); - aFunction.invoke(aParamArr); + var tmpFunction = new Function(aComp, aMethodName, aTypeArr); + tmpFunction.invoke(aParamArr); } - catch(NoSuchMethodException aExp1) + catch (NoSuchMethodException aExp1) { throw new RuntimeException("Failed to locate valid function. Method:" + aMethodName, aExp1); } - catch(Exception aExp2) + catch (Exception aExp2) { throw new RuntimeException("Failed to execute function. Method:" + aMethodName, aExp2); } @@ -548,34 +497,44 @@ public class GuiUtil return; // Recurse down our children - subCompArr = ((Container)aComp).getComponents(); + var subCompArr = ((Container) aComp).getComponents(); for (Component aSubComp : subCompArr) callMethodHelper(aSubComp, refMatchClass, aMethodName, aTypeArr, aParamArr); } /** - * Utility method to set all subcomponents to the specified enabled mode. + * Utility method to recursively change the enable state of all Components contained by the specified Container. + * + * @param aContainer + * The Container of interest. + * @param aBool + * Boolean used to define the enable state. */ - // TODO: Phase this method out, replace with callMethod() - public static void setEnabled(Component aComp, boolean aBool) + public static void setEnabled(Container aContainer, boolean aBool) { - Component[] subCompArr; - - aComp.setEnabled(aBool); - if (aComp instanceof Container == false) - return; - - subCompArr = ((Container)aComp).getComponents(); - for (Component aSubComp : subCompArr) - GuiUtil.setEnabled(aSubComp, aBool); + for (Component aComp : aContainer.getComponents()) + { + aComp.setEnabled(aBool); + if (aComp instanceof Container) + setEnabled((Container) aComp, aBool); + } } /** - * Utility method to set the enabled switch on all of the specified components. + * Utility method to set the enable state on all of the specified components. */ - public static void setEnabled(boolean aBool, Component... componentArr) + public static void setEnabled(boolean aBool, Component... aComponentArr) { - for (Component aComp : componentArr) + for (Component aComp : aComponentArr) + aComp.setEnabled(aBool); + } + + /** + * Utility method to set the enable state on all of the specified components. + */ + public static void setEnabled(boolean aBool, Collection aComponentC) + { + for (Component aComp : aComponentC) aComp.setEnabled(aBool); } @@ -592,43 +551,48 @@ public class GuiUtil } /** - * Utility method that takes up to 8 buttons and converts all of their selection states to a single byte. + * Utility method that returns a bitmask composed of the selection state of the specified buttons. + *

+ * The array is transformed such that the least significant bit is assumed to have index position 0 with the higher + * bits corresponding to a larger (array) index. + *

+ * A maximum of 32 buttons is support. Passing in more than 32 buttons will result in an exception. */ - public static byte getSelectionStateAsByte(AbstractButton... buttonArr) + public static int getSelectionStateAsBitMask(AbstractButton... aButtonArr) { - byte retByte; + if (aButtonArr.length > 32) + throw new RuntimeException("Improper API call. Max of 32 buttons supported. Passed: " + aButtonArr.length); - if (buttonArr.length > 8) - throw new RuntimeException("Improper API call. Max of 8 buttons supported. Passed: " + buttonArr.length); - - retByte = 0; - for (int c1 = 0; c1 < buttonArr.length; c1++) + int retBitMask = 0; + for (int c1 = 0; c1 < aButtonArr.length; c1++) { - if (buttonArr[c1].isSelected() == true) - retByte |= 1 << c1; + if (aButtonArr[c1].isSelected() == true) + retBitMask |= 1 << c1; } - return retByte; + return retBitMask; } /** - * Utility method that takes up to 8 buttons and configures the selection state of the buttons to match the bit - * pattern of aByte. + * Utility method that updates the selection state of the provided buttons to reflect the specified bitmask. + *

+ * The array is transformed such that the least significant bit is assumed to have index position 0 with the higher + * bits corresponding to a larger (array) index. + *

+ * A maximum of 32 buttons is support. Passing in more than 32 buttons will result in an exception. */ - public static void setSelectionState(byte aByte, AbstractButton... buttonArr) + public static void setSelectionStateFromBitMask(int aBitMask, AbstractButton... aButtonArr) { - boolean aBool; + if (aButtonArr.length > 32) + throw new RuntimeException("Improper API call. Max of 32 buttons supported. Passed: " + aButtonArr.length); - if (buttonArr.length > 8) - throw new RuntimeException("Improper API call. Max of 8 buttons supported. Passed: " + buttonArr.length); - - for (int c1 = 0; c1 < buttonArr.length; c1++) + for (int c1 = 0; c1 < aButtonArr.length; c1++) { - aBool = false; - if (((0x01 << c1) & aByte) != 0) - aBool = true; + var tmpBool = false; + if (((0x01 << c1) & aBitMask) != 0) + tmpBool = true; - buttonArr[c1].setSelected(aBool); + aButtonArr[c1].setSelected(tmpBool); } } @@ -637,9 +601,7 @@ public class GuiUtil */ public static void updateSlider(JSlider aSlider, int aVal) { - ChangeListener[] tmpArr; - - tmpArr = aSlider.getChangeListeners(); + var tmpArr = aSlider.getChangeListeners(); for (ChangeListener aListener : tmpArr) aSlider.removeChangeListener(aListener); @@ -654,13 +616,13 @@ public class GuiUtil * Utility method that checks to ensure the current thread is running on the ATW thread. If it is NOT then the * specified Runnable will be posted so that it is called on the AWT thread. If it is running on the AWT thread then * nothing will happen and this method will return false. - *

+ *

* Typically this utility method is called at the start of a function to ensure it is on the AWT thread, and if not * then schedule the function onto the AWT thread. Thus it is strongly advisable that if this method returns true the * caller should immediately exit. - *

+ *

* Typical usage within a method: - * + * *

 	 * public void actionPerformed(aEvent)
 	 * {
@@ -668,7 +630,7 @@ public class GuiUtil
 	 *    Runnable tmpRunnable = ()-> actionPerformed(aEvent);
 	 *    if (redispatchOnAwtIfNeeded(this, "actionPerformed", aEvent) = true)
 	 *       return;
-	 *       
+	 *
 	 *    // Do normal work ...
 	 * }
 	 * 
diff --git a/src/glum/gui/GuiUtilEx.java b/src/glum/gui/GuiUtilEx.java new file mode 100644 index 0000000..6dbae23 --- /dev/null +++ b/src/glum/gui/GuiUtilEx.java @@ -0,0 +1,88 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui; + +import java.awt.*; + +/** + * Collection of additional AWT/Swing based utility methods. + * + * @author lopeznr1 + */ +public class GuiUtilEx +{ + /** + * Utility method that returns the default dimension for the main window. + *

+ * The returned {@link Dimension} will have a minimum size of [aMinX,aMinY] or [80%,80%] of the screen resolution + * (which ever is smaller). + */ + public static Dimension getDimensionDefaultMain(int aMinX, int aMinY) + { + var screenDim = Toolkit.getDefaultToolkit().getScreenSize(); + int winW = Math.min(aMinX, (int) (screenDim.getWidth() * 0.80)); + int winH = Math.min(aMinY, (int) (screenDim.getHeight() * 0.80)); + return new Dimension(winW, winH); + } + + /** + * Utility method that returns the default dimension for the main window. + *

+ * The returned {@link Dimension} will have a minimum size of [950, 750] or 80% of the screen resolution. + */ + public static Dimension getDimensionDefaultMain() + { + // Delegate + return getDimensionDefaultMain(950, 750); + } + + /** + * Utility method that returns a new Dimension where the size has been scaled by the specified scalars. + * + * @param aDimension + * The original dimension + * @param aScalerX + * Percent to scale the dimensions width. + * @param aScalerY + * Percent to scale the dimensions height. + */ + public static Dimension getDimensionScaled(Dimension aDimension, double aScalerX, double aScalerY) + { + int winW = Math.min(950, (int) (aDimension.getWidth() * aScalerX)); + int winH = Math.min(750, (int) (aDimension.getHeight() * aScalerY)); + return new Dimension(winW, winH); + } + + /** + * Utility method that returns the (first) parent that matches the specified Class. If there is no such component + * then null will be returned. + */ + public static Component getParent(Component aComp, Class aClass) + { + // Search through all the (grand)parents + var parent = aComp.getParent(); + while (parent != null) + { + // Bail once we have a matching class + if (parent.getClass() == aClass) + return parent; + + // Next parent + parent = parent.getParent(); + } + + return null; + } + +} diff --git a/src/glum/gui/TableUtil.java b/src/glum/gui/TableUtil.java new file mode 100644 index 0000000..e959018 --- /dev/null +++ b/src/glum/gui/TableUtil.java @@ -0,0 +1,185 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui; + +import java.util.*; + +import javax.swing.JTable; +import javax.swing.event.*; + +import com.google.common.primitives.Ints; + +import glum.gui.table.TableSorter; +import glum.item.ItemManager; + +/** + * Collection of utilities associated with JTables. + * + * @author lopeznr1 + */ +public class TableUtil +{ + + /** + * Utility method to invert the selection on the specified table. The selection will be inverted with the specified + * ListSelectionListener being ignored. + * + * @param aTable + * The table for which the selection should be inverted. + * @param aIgnoreListener + * An ListSelectionListener that will not receive any intermediate events. It is important that this listener + * be registered with the table's selection model as it will be deregistered and then later registered. If + * null then this argument will be ignored. + */ + public static void invertSelection(JTable aTable, ListSelectionListener aIgnoreListener) + { + Set oldSet = new HashSet<>(); + for (int aId : aTable.getSelectedRows()) + oldSet.add(aId); + + int numRows = aTable.getRowCount(); + int[] tmpArr = new int[numRows - oldSet.size()]; + + // Determine the rows that are to be selected + int idx = 0; + for (int aId = 0; aId < numRows; aId++) + { + // Skip to next if row was previously selected + if (oldSet.contains(aId) == true) + continue; + + tmpArr[idx] = aId; + idx++; + } + + // Delegate + setSortedSelection(aTable, aIgnoreListener, tmpArr); + + // Send out a single event of the change + if (aIgnoreListener != null) + aIgnoreListener.valueChanged(new ListSelectionEvent(aTable, 0, aTable.getRowCount() - 1, false)); + } + + /** + * Utility method to select the rows at the specified indexes. The selection will be updated with the specified + * ListSelectionListener being ignored. + * + * @param aTable + * The table for which the selection should be updated. + * @param aIgnoreListener + * A ListSelectionListener that will not receive any intermediate events. It is important that this listener + * be registered with the table's selection model as it will be deregistered and then later registered. If + * null then this argument will be ignored. + * @param aRowL + * A list of indexes corresponding to the rows that are to be selected. All other rows will be unselected. + */ + public static void setSelection(JTable aTable, ListSelectionListener aIgnoreListener, List aRowL) + { + // Transform to a sorted array + int[] rowArr = Ints.toArray(aRowL); + Arrays.parallelSort(rowArr); + + // Delegate + setSortedSelection(aTable, aIgnoreListener, rowArr); + } + + /** + * Utility method that will synchronize the table selection to match the selected items in the ItemManager. If new + * items were selected then the table will be scrolled (to the first newly selected row). + */ + public static void updateTableSelection(ListSelectionListener aIgnoreListener, ItemManager aManager, + JTable aTable, TableSorter aSortTableModel) + { + // Form a reverse lookup map of item to (view) index + List fullItemL = aManager.getAllItems(); + Map revLookM = new HashMap<>(); + for (int aIdx = 0; aIdx < fullItemL.size(); aIdx++) + { + int tmpIdx = aSortTableModel.viewIndex(aIdx); + revLookM.put(fullItemL.get(aIdx), tmpIdx); + } + + int[] idxArr = aTable.getSelectedRows(); + List oldL = Ints.asList(idxArr); + Set oldS = new LinkedHashSet<>(oldL); + + List newL = new ArrayList<>(); + for (G1 aItem : aManager.getSelectedItems()) + newL.add(revLookM.get(aItem)); + Set newS = new LinkedHashSet<>(newL); + + // Bail if nothing has changed + if (newS.equals(oldS) == true) + return; + + // Update the table's selection + setSelection(aTable, aIgnoreListener, newL); + aTable.repaint(); + } + + /** + * Utility helper method that selects the specified rows. + *

+ * The rows must be in sorted order. + * + * @param aTable + * The table for which the selection should be updated. + * @param aIgnoreListener + * An ListSelectionListener that will not receive any intermediate events. It is important that this listener + * be registered with the table's selection model as it will be deregistered and then later registered. If + * null then this argument will be ignored. + * @param aSortedRowArr + * An array of indexes corresponding to the rows that are to be selected. All other rows will be unselected. + */ + private static void setSortedSelection(JTable aTable, ListSelectionListener aIgnoreListener, int[] aSortedRowArr) + { + // Initial range + int begIdx = -1; + int endIdx = -1; + if (aSortedRowArr.length >= 1) + { + begIdx = aSortedRowArr[0]; + endIdx = begIdx; + } + + if (aIgnoreListener != null) + aTable.getSelectionModel().removeListSelectionListener(aIgnoreListener); + + aTable.clearSelection(); + + for (int aRow : aSortedRowArr) + { + // Expand the range by: +1 + if (aRow == endIdx + 1) + { + endIdx++; + continue; + } + + // Add the current interval + aTable.addRowSelectionInterval(begIdx, endIdx); + + // Start a new range + begIdx = endIdx = aRow; + } + + // Ensure the last interval gets added + if (begIdx != -1 && endIdx != -1) + aTable.addRowSelectionInterval(begIdx, endIdx); + + if (aIgnoreListener != null) + aTable.getSelectionModel().addListSelectionListener(aIgnoreListener); + } + +} diff --git a/src/glum/gui/action/ActionComponentProvider.java b/src/glum/gui/action/ActionComponentProvider.java new file mode 100644 index 0000000..7ca2657 --- /dev/null +++ b/src/glum/gui/action/ActionComponentProvider.java @@ -0,0 +1,33 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.action; + +import java.awt.Component; +import java.util.Collection; + +/** + * Interface that provides a mechanism for a component to declare that it will provide action {@link Component}s + * (typically buttons). + * + * @author lopeznr1 + */ +public interface ActionComponentProvider +{ + + /** + * Returns a list of {@link Component}s that should be placed in the action region. + */ + public Collection getActionButtons(); + +} diff --git a/src/glum/gui/action/ClickAction.java b/src/glum/gui/action/ClickAction.java index 1a230db..a75e16d 100644 --- a/src/glum/gui/action/ClickAction.java +++ b/src/glum/gui/action/ClickAction.java @@ -1,22 +1,44 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.action; -import java.awt.event.*; -import javax.swing.*; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; +import javax.swing.AbstractButton; + +/** + * Action used to "click" on the target {@link AbstractButton}. + *

+ * Clicking will be done via the {@link AbstractButton#doClick()} method. + * + * @author lopeznr1 + */ public class ClickAction extends AbstractAction { - // State vars - protected AbstractButton target; + // Reference vars + private final AbstractButton refTarget; public ClickAction(AbstractButton aTarget) { - target = aTarget; + refTarget = aTarget; } @Override public void actionPerformed(ActionEvent e) { - target.doClick(); + refTarget.doClick(); } } diff --git a/src/glum/gui/action/CloseDialog.java b/src/glum/gui/action/CloseDialog.java new file mode 100644 index 0000000..7aed8e9 --- /dev/null +++ b/src/glum/gui/action/CloseDialog.java @@ -0,0 +1,82 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.action; + +import java.awt.Component; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; + +import javax.swing.*; + +import glum.gui.GuiUtil; +import net.miginfocom.swing.MigLayout; + +/** + * JDialog that holds a single {@link Component} and a close button. + * + * @author lopeznr1 + */ +public class CloseDialog extends JDialog implements ActionListener +{ + // Gui vars + private final JButton closeB; + + /** + * Standard Constructor + */ + public CloseDialog(Frame aParent, Component aMainComp) + { + super(aParent); + + var tmpPanel = new JPanel(); + tmpPanel.setLayout(new MigLayout("", "[]", "0[]0[]")); + + tmpPanel.add(aMainComp, "growx,growy,pushx,pushy,span,wrap"); + + // Form a unified list of buttons + var tmpActionCompL = new ArrayList(); + if (aMainComp instanceof ActionComponentProvider) + tmpActionCompL.addAll(((ActionComponentProvider) aMainComp).getActionButtons()); + closeB = GuiUtil.formButton(this, "Close"); + tmpActionCompL.add(closeB); + + // Add the components in + var isFirst = true; + for (Component aComp : tmpActionCompL) + { + if (isFirst == true) + tmpPanel.add(aComp, "span,split,ax right"); + else + tmpPanel.add(aComp, ""); + + isFirst = false; + } + + setContentPane(tmpPanel); + setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); + setModal(false); + pack(); + } + + @Override + public void actionPerformed(ActionEvent aEvent) + { + var source = aEvent.getSource(); + if (source == closeB) + setVisible(false); + } + +} diff --git a/src/glum/gui/action/MakeVisibleAction.java b/src/glum/gui/action/MakeVisibleAction.java index c63fead..1933288 100644 --- a/src/glum/gui/action/MakeVisibleAction.java +++ b/src/glum/gui/action/MakeVisibleAction.java @@ -1,23 +1,55 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.action; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; +import java.awt.Component; +import java.awt.event.ActionEvent; +import javax.swing.AbstractAction; + +/** + * Action used to change the target {@link Component} to be visible. + * + * @author lopeznr1 + */ public class MakeVisibleAction extends AbstractAction { - // State vars - protected Component target; + // Reference vars + private final Component refTarget; + /** + * Standard Constructor + * + * @param aTarget + */ public MakeVisibleAction(Component aTarget) { - target = aTarget; + refTarget = aTarget; + } + + /** + * Returns the reference target. + */ + public Component getTarget() + { + return refTarget; } @Override public void actionPerformed(ActionEvent e) { - target.setVisible(true); + refTarget.setVisible(true); } } diff --git a/src/glum/gui/action/PopAction.java b/src/glum/gui/action/PopAction.java new file mode 100644 index 0000000..aceca5e --- /dev/null +++ b/src/glum/gui/action/PopAction.java @@ -0,0 +1,66 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.action; + +import java.awt.event.ActionEvent; +import java.util.Collection; +import java.util.List; + +import javax.swing.AbstractAction; +import javax.swing.JMenuItem; + +import com.google.common.collect.ImmutableList; + +/** + * Base action specific to popup menus. + *

+ * Whenever the list of selected objects changes this PopAction will be notified. + * + * @author lopeznr1 + */ +public abstract class PopAction extends AbstractAction +{ + // State vars + private ImmutableList itemL; + + /** + * Standard Constructor + */ + public PopAction() + { + itemL = ImmutableList.of(); + } + + /** + * Notification that the {@link PopAction} should be executed on the specified items. + * + * @param aItemL + */ + public abstract void executeAction(List aItemL); + + /** + * Sets in the items that are currently selected. + */ + public void setChosenItems(Collection aItemC, JMenuItem aAssocMI) + { + itemL = ImmutableList.copyOf(aItemC); + } + + @Override + public void actionPerformed(ActionEvent aAction) + { + executeAction(itemL); + } + +} diff --git a/src/glum/gui/action/PopupMenu.java b/src/glum/gui/action/PopupMenu.java new file mode 100644 index 0000000..a391fc4 --- /dev/null +++ b/src/glum/gui/action/PopupMenu.java @@ -0,0 +1,93 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.action; + +import java.awt.Component; +import java.util.*; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import glum.item.ItemManager; + +/** + * UI component that allows a custom popup menu to be built. + * + * @author lopeznr1 + */ +public class PopupMenu extends JPopupMenu +{ + // Reference vars + private ItemManager refManager; + + // State vars + private Map> actionM; + + /** + * Standard Constructor + * + * @param aManager + */ + public PopupMenu(ItemManager aManager) + { + refManager = aManager; + + actionM = new HashMap<>(); + } + + /** + * Registers the specified {@link PopAction} into this {@link PopupMenu}. + *

+ * A simple menu item will be created and associated with the specified action. + */ + public void installPopAction(PopAction aAction, String aTitle) + { + JMenuItem tmpMI = new JMenuItem(aAction); + tmpMI.setText(aTitle); + + // Delegate + installPopAction(aAction, tmpMI); + } + + /** + * Registers the specified {@link PopAction} into this {@link PopupMenu}. + *

+ * The action will be associated with the specified menu item. + */ + public void installPopAction(PopAction aAction, JMenuItem aTargMI) + { + add(aTargMI); + actionM.put(aTargMI, aAction); + } + + @Override + public void show(Component aParent, int aX, int aY) + { + // Bail if we do not have selected items + Set tmpS = refManager.getSelectedItems(); + if (tmpS.size() == 0) + return; + + // Update our PopActions + for (JMenuItem aMI : actionM.keySet()) + { + PopAction tmpPA = actionM.get(aMI); + tmpPA.setChosenItems(tmpS, aMI); + } + + // Delegate + super.show(aParent, aX, aY); + } + +} diff --git a/src/glum/gui/component/GBaseTextField.java b/src/glum/gui/component/GBaseTextField.java new file mode 100644 index 0000000..07657d2 --- /dev/null +++ b/src/glum/gui/component/GBaseTextField.java @@ -0,0 +1,80 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.component; + +import java.awt.*; + +import javax.swing.JTextField; + +/** + * User interface input used to capture an input string. + *

+ * This object provides the following additional features: + *

    + *
  • Display of passive hint text + *
+ * + * @author lopeznr1 + */ +public class GBaseTextField extends JTextField +{ + // State vars + private String mHint; + + /** Standard Constructor */ + public GBaseTextField(String aText, int aNumColumns) + { + super(aText, aNumColumns); + + mHint = null; + } + + /** + * Sets a hint that will be shown whenever the text field is empty. + */ + public void setHint(String aHint) + { + mHint = aHint; + repaint(); + } + + @Override + public void paint(Graphics g) + { + super.paint(g); + + // Bail if there is already input + if (getText().length() != 0) + return; + + // Bail if there is no hint + if (mHint == null || mHint.length() == 0) + return; + + // Draw the textual hint + // Source: + // https://stackoverflow.com/questions/1738966/java-jtextfield-with-input-hint + int h = getHeight(); + ((Graphics2D) g).setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); + Insets ins = getInsets(); + FontMetrics fm = g.getFontMetrics(); + int c0 = getBackground().getRGB(); + int c1 = getForeground().getRGB(); + int m = 0xfefefefe; + int c2 = ((c0 & m) >>> 1) + ((c1 & m) >>> 1); + g.setColor(new Color(c2, true)); + g.drawString(mHint, ins.left, h / 2 + fm.getAscent() / 2 - 2); + } + +} diff --git a/src/glum/gui/component/GComboBox.java b/src/glum/gui/component/GComboBox.java index 4682a0e..e756a73 100644 --- a/src/glum/gui/component/GComboBox.java +++ b/src/glum/gui/component/GComboBox.java @@ -1,58 +1,81 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; -import glum.gui.component.model.GComboBoxModel; - import java.awt.event.ActionListener; -import java.util.*; +import java.util.Collection; +import java.util.List; + import javax.swing.JComboBox; import javax.swing.ListCellRenderer; +import com.google.common.collect.ImmutableList; + +import glum.gui.component.model.GComboBoxModel; + +/** + * Enhanced implementation of {@link JComboBox}. This implementation provides for better generics support. Please + * utilize the {@link #getChosenItem()}, {@link #setChosenItem(Object)}, etc... + * + * @author lopeznr1 + */ public class GComboBox extends JComboBox { // State vars - protected GComboBoxModel itemModel; - + private GComboBoxModel itemModel; + public GComboBox() { - itemModel = new GComboBoxModel(new LinkedList()); + itemModel = new GComboBoxModel(ImmutableList.of()); setModel(itemModel); } - + public GComboBox(ActionListener aListener, ListCellRenderer aRenderer) { this(); - + addActionListener(aListener); setRenderer(aRenderer); } - + public GComboBox(ActionListener aListener) { this(); - + addActionListener(aListener); } - - public GComboBox(ActionListener aListener, List aItemList) + + public GComboBox(ActionListener aListener, Collection aItemC) { - itemModel = new GComboBoxModel(aItemList); + itemModel = new GComboBoxModel<>(aItemC); setModel(itemModel); addActionListener(aListener); } - + public GComboBox(ActionListener aListener, G1... aItemArr) { - itemModel = new GComboBoxModel(aItemArr); + itemModel = new GComboBoxModel<>(aItemArr); setModel(itemModel); - + addActionListener(aListener); } /** * Returns the list of all items stored in the GComboBox */ - public ArrayList getAllItems() + public List getAllItems() { return itemModel.getAllItems(); } @@ -64,43 +87,43 @@ public class GComboBox extends JComboBox { return itemModel.getSelectedItem(); } - + /** * Sets in the currently selected item. This method will not trigger an ActionEvent. + * * @see JComboBox#setSelectedItem */ public void setChosenItem(G1 aItem) { - ActionListener[] listenerArr; - - listenerArr = getActionListeners(); + var listenerArr = getActionListeners(); for (ActionListener aListener : listenerArr) removeActionListener(aListener); - + itemModel.setSelectedItem(aItem); super.setSelectedItem(aItem); for (ActionListener aListener : listenerArr) addActionListener(aListener); - // We must force a repaint since any ActionListener responsible will never get the update + // We must force a repaint since any ActionListener responsible will never get the update repaint(); } - + /** * Note aItem must be of the generified type. This method will not trigger an ActionEvent. */ - @Override @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public void addItem(Object aItem) { ActionListener[] listenerArr; - + listenerArr = getActionListeners(); for (ActionListener aListener : listenerArr) removeActionListener(aListener); - - itemModel.addItem((G1)aItem); - + + itemModel.addItem((G1) aItem); + for (ActionListener aListener : listenerArr) addActionListener(aListener); } @@ -110,15 +133,29 @@ public class GComboBox extends JComboBox */ public void addItems(G1... aItemArr) { - ActionListener[] listenerArr; - - listenerArr = getActionListeners(); + var listenerArr = getActionListeners(); for (ActionListener aListener : listenerArr) removeActionListener(aListener); - + for (G1 aItem : aItemArr) itemModel.addItem(aItem); - + + for (ActionListener aListener : listenerArr) + addActionListener(aListener); + } + + /** + * Adds all of the specified items to the model. This method will not trigger an ActionEvent. + */ + public void addItems(Collection aItemC) + { + var listenerArr = getActionListeners(); + for (ActionListener aListener : listenerArr) + removeActionListener(aListener); + + for (G1 aItem : aItemC) + itemModel.addItem(aItem); + for (ActionListener aListener : listenerArr) addActionListener(aListener); } @@ -126,17 +163,68 @@ public class GComboBox extends JComboBox /** * Note aItem must be of the generified type. This method will not trigger an ActionEvent. */ - @Override @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public void removeItem(Object aItem) { - ActionListener[] listenerArr; - - listenerArr = getActionListeners(); + var listenerArr = getActionListeners(); for (ActionListener aListener : listenerArr) removeActionListener(aListener); - - itemModel.removeItem((G1)aItem); - + + itemModel.removeItem((G1) aItem); + + for (ActionListener aListener : listenerArr) + addActionListener(aListener); + } + + /** + * Method to replace all of the items with the specified list of items. + *

+ * If the current selection is in the replacement list then the selection will be maintained otherwise it will be + * replaced with the first item in the list. + *

+ * This method will not trigger an ActionEvent. + */ + public void replaceAllItems(Collection aItemC) + { + G1 pickItem = getChosenItem(); + + var listenerArr = getActionListeners(); + for (ActionListener aListener : listenerArr) + removeActionListener(aListener); + + // Replace the items + itemModel.removeAllItems(); + + for (G1 aItem : aItemC) + itemModel.addItem(aItem); + + // Update the selection after the replacement + if (aItemC.contains(pickItem) == false) + pickItem = null; + if (pickItem == null && aItemC.size() > 0) + pickItem = aItemC.iterator().next(); + + setChosenItem(pickItem); + + for (ActionListener aListener : listenerArr) + addActionListener(aListener); + } + + /** + * Method to replace the original item with the provided replacement. + *

+ * This method will not trigger an ActionEvent. + */ + public void replaceItem(G1 aOrigItem, G1 aReplItem) + { + var listenerArr = getActionListeners(); + for (ActionListener aListener : listenerArr) + removeActionListener(aListener); + + // Delegate + itemModel.replaceItem(aOrigItem, aReplItem); + for (ActionListener aListener : listenerArr) addActionListener(aListener); } @@ -147,14 +235,12 @@ public class GComboBox extends JComboBox @Override public void removeAllItems() { - ActionListener[] listenerArr; - - listenerArr = getActionListeners(); + var listenerArr = getActionListeners(); for (ActionListener aListener : listenerArr) removeActionListener(aListener); - + itemModel.removeAllItems(); - + for (ActionListener aListener : listenerArr) addActionListener(aListener); } @@ -166,7 +252,7 @@ public class GComboBox extends JComboBox // { // throw new RuntimeException("Unsupported operation. Call getChosenItem()"); // } -// +// // @Override // public void setSelectedItem(Object aObj) // { diff --git a/src/glum/gui/component/GComponent.java b/src/glum/gui/component/GComponent.java index 6b26ae1..0bf23e9 100644 --- a/src/glum/gui/component/GComponent.java +++ b/src/glum/gui/component/GComponent.java @@ -1,32 +1,49 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; -import glum.gui.panel.generic.GenericCodes; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.util.ArrayList; import java.util.List; + import javax.swing.JComponent; -import com.google.common.collect.Lists; +import glum.gui.panel.generic.GenericCodes; +/** + * Base component that provides support for {@link ActionListener} event mechanism. + * + * @author lopeznr1 + */ public class GComponent extends JComponent implements GenericCodes { // State vars - protected List myListeners; + protected List listenerL; + /** Standard Constructor */ public GComponent() { - super(); - - myListeners = Lists.newLinkedList(); + listenerL = new ArrayList<>(); } - + /** * Add an ActionListener to this GPanel */ public void addActionListener(ActionListener aListener) { - myListeners.add(aListener); + listenerL.add(aListener); } /** @@ -34,16 +51,16 @@ public class GComponent extends JComponent implements GenericCodes */ public void removeActionListener(ActionListener aListener) { - myListeners.remove(aListener); + listenerL.remove(aListener); } - + /** * Send out notification to all of the ActionListeners */ public void notifyListeners(Object aSource, int aId, String aCommand) { - for (ActionListener aListener : myListeners) + for (var aListener : listenerL) aListener.actionPerformed(new ActionEvent(aSource, aId, aCommand)); } - + } diff --git a/src/glum/gui/component/GFancyLabel.java b/src/glum/gui/component/GFancyLabel.java index c787cc0..2f6e06c 100644 --- a/src/glum/gui/component/GFancyLabel.java +++ b/src/glum/gui/component/GFancyLabel.java @@ -1,25 +1,37 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.Dimension; import java.awt.Font; +import javax.swing.JLabel; + import glum.unit.UnitListener; import glum.unit.UnitProvider; -import javax.swing.JLabel; - public class GFancyLabel extends JLabel implements UnitListener { // State vars - protected UnitProvider[] unitProviderArr; - protected String[] formatStrArr; - protected Object[] refValueArr; + private UnitProvider[] unitProviderArr; + private String[] formatStrArr; + private Object[] refValueArr; /** - * Fancy JLabel which provides auto formatting of objects. The constructor - * is provided with a formatStr which has unit place holders specified by "%u" - * There must be a corresponding UnitProvider for each occurrence of "%u". This - * is provided via the variable arguments of UnitProvider. + * Fancy JLabel which provides auto formatting of objects. The constructor is provided with a formatStr which has + * unit place holders specified by "%u" There must be a corresponding UnitProvider for each occurrence of "%u". This + * is provided via the variable arguments of UnitProvider. */ public GFancyLabel(String formatStr, UnitProvider... aUnitProviderArr) { @@ -27,27 +39,25 @@ public class GFancyLabel extends JLabel implements UnitListener } /** - * Fancy JLabel which provides auto formatting of objects. The constructor - * is provided with a formatStr which has unit place holders specified by "%u" - * There must be a corresponding UnitProvider for each occurrence of "%u". This - * is provided via the variable arguments of UnitProvider. + * Fancy JLabel which provides auto formatting of objects. The constructor is provided with a formatStr which has + * unit place holders specified by "%u" There must be a corresponding UnitProvider for each occurrence of "%u". This + * is provided via the variable arguments of UnitProvider. */ public GFancyLabel(Font aFont, String aFormatStr, UnitProvider... aUnitProviderArr) { - super(); - if (aFont != null) setFont(aFont); - + formatStrArr = aFormatStr.split("%u", -1); - + unitProviderArr = aUnitProviderArr; for (UnitProvider aUnitProvider : unitProviderArr) aUnitProvider.addListener(this); - + // Insanity check if (unitProviderArr.length != formatStrArr.length - 1) - throw new RuntimeException("Num place holders: " + (formatStrArr.length - 1) + " Num units: " + unitProviderArr.length); + throw new RuntimeException( + "Num place holders: " + (formatStrArr.length - 1) + " Num units: " + unitProviderArr.length); refValueArr = new Object[unitProviderArr.length]; for (int c1 = 0; c1 < unitProviderArr.length; c1++) @@ -63,31 +73,30 @@ public class GFancyLabel extends JLabel implements UnitListener } /** - * Method to set in the set of values which will be formatted with the associated UnitProviders - * which were specified via the constructor. + * Method to set in the set of values which will be formatted with the associated UnitProviders which were specified + * via the constructor. */ public void setValues(Object... aValueArr) { - String aStr; - // Ensure the number of objects matches the number of units if (unitProviderArr.length != aValueArr.length) - throw new RuntimeException("Inproper number of arguments. Expected: " + unitProviderArr.length + " Recieved:" + aValueArr.length); - + throw new RuntimeException("Inproper number of arguments. Expected: " + unitProviderArr.length // + + " Recieved:" + aValueArr.length); + for (int c1 = 0; c1 < aValueArr.length; c1++) refValueArr[c1] = aValueArr[c1]; - - aStr = ""; + + var tmpStr = ""; for (int c1 = 0; c1 < aValueArr.length; c1++) { - aStr += formatStrArr[c1]; - aStr += unitProviderArr[c1].getUnit().getString(aValueArr[c1], false); + tmpStr += formatStrArr[c1]; + tmpStr += unitProviderArr[c1].getUnit().getString(aValueArr[c1], false); } - - if (formatStrArr.length > aValueArr.length) - aStr += formatStrArr[formatStrArr.length - 1]; - setText(aStr); + if (formatStrArr.length > aValueArr.length) + tmpStr += formatStrArr[formatStrArr.length - 1]; + + setText(tmpStr); } } diff --git a/src/glum/gui/component/GLabel.java b/src/glum/gui/component/GLabel.java index 005ba91..0feb674 100644 --- a/src/glum/gui/component/GLabel.java +++ b/src/glum/gui/component/GLabel.java @@ -1,15 +1,25 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.Dimension; import java.awt.Font; -import glum.unit.ConstUnitProvider; -import glum.unit.Unit; -import glum.unit.UnitListener; -import glum.unit.UnitProvider; - import javax.swing.JLabel; +import glum.unit.*; + public class GLabel extends JLabel implements UnitListener { // State vars @@ -34,8 +44,6 @@ public class GLabel extends JLabel implements UnitListener public GLabel(UnitProvider aUnitProvider, Font aFont, boolean aShowLabel) { - super(); - refUnitProvider = aUnitProvider; if (refUnitProvider != null) refUnitProvider.addListener(this); @@ -62,9 +70,9 @@ public class GLabel extends JLabel implements UnitListener { String aStr; Unit aUnit; - + refValue = aValue; - + aUnit = null; if (refUnitProvider != null) aUnit = refUnitProvider.getUnit(); diff --git a/src/glum/gui/component/GList.java b/src/glum/gui/component/GList.java index fbe2491..ad30bc8 100644 --- a/src/glum/gui/component/GList.java +++ b/src/glum/gui/component/GList.java @@ -1,22 +1,30 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import javax.swing.JComponent; -import javax.swing.JList; -import javax.swing.SwingUtilities; +import java.util.*; + +import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import glum.gui.component.model.GListModel; import glum.gui.panel.generic.GenericCodes; -import com.google.common.collect.Lists; - public class GList extends JComponent implements GenericCodes, ListSelectionListener { // Gui vars @@ -52,9 +60,7 @@ public class GList extends JComponent implements GenericCodes, ListSelection */ public G1 getSelectedItem() { - int selectedIndex; - - selectedIndex = refList.getSelectedIndex(); + var selectedIndex = refList.getSelectedIndex(); if (selectedIndex == -1) return null; @@ -66,17 +72,14 @@ public class GList extends JComponent implements GenericCodes, ListSelection */ public List getSelectedItems() { - ArrayList retList; - int[] indexArr; + var indexArr = refList.getSelectedIndices(); - indexArr = refList.getSelectedIndices(); - - retList = Lists.newArrayList(); + var retItemL = new ArrayList(); for (int aIndex : indexArr) - retList.add(refModel.getElementAt(aIndex)); + retItemL.add(refModel.getElementAt(aIndex)); - retList.trimToSize(); - return retList; + retItemL.trimToSize(); + return retItemL; } /** @@ -104,17 +107,14 @@ public class GList extends JComponent implements GenericCodes, ListSelection */ public void setSelectedItems(List aItemList) { - int[] idArr; - int c1; - // Ensure we are executed only on the proper thread if (SwingUtilities.isEventDispatchThread() == false) throw new RuntimeException("GList.selectItems() not executed on the AWT event dispatch thread."); refList.removeListSelectionListener(this); - c1 = 0; - idArr = new int[aItemList.size()]; + var c1 = 0; + var idArr = new int[aItemList.size()]; for (G1 aItem : aItemList) { idArr[c1] = refModel.indexOf(aItem); diff --git a/src/glum/gui/component/GNumberField.java b/src/glum/gui/component/GNumberField.java index 5ed1f85..f79f740 100644 --- a/src/glum/gui/component/GNumberField.java +++ b/src/glum/gui/component/GNumberField.java @@ -1,56 +1,86 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.Color; import java.awt.event.ActionListener; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.text.*; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.Document; + +import com.google.common.collect.Range; import glum.gui.document.NumberDocument; -import glum.unit.ConstUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.Unit; -import glum.unit.UnitListener; -import glum.unit.UnitProvider; +import glum.unit.*; -public class GNumberField extends JTextField implements DocumentListener, UnitListener +/** + * User interface input used to capture an individual numerical input (type: double). + *

+ * Unlike JTextField, users of this class should not use getText() / setText() but rather getValue() / setValue() + * methods. Also it should not be necessary to register DocumentListeners - rather an ActionListener should be + * sufficient. + *

+ * This class provides two modes of converting model values to textual input: + *

    + *
  • {@link NumberFormat} mechanism + *
  • {@link UnitProvider} mechanism + *
+ * + * @author lopeznr1 + */ +public class GNumberField extends GBaseTextField implements DocumentListener, UnitListener { + // Attributes + private final NumberFormat refFormat; + private final UnitProvider refUnitProvider; + // State vars - protected UnitProvider refUnitProvider; - protected double currValue, minValue, maxValue; - protected boolean isMutating; + private Range minMaxRange; + private double currValue; + private boolean isMutating; // Gui vars - protected Color failColor, passColor; - protected NumberDocument myDocument; + private Color colorFail, colorPass; + private NumberDocument myDocument; /** - * Constructor - * + * Standard Constructor + * * @param aListener - * : Default ActionListener + * An ActionListener that will be notified when ever the user makes any input changes. * @param aUnit - * : Object used to format programatic entered values. Note aUnitProvider will also be used to determine if - * Floating or only Integral input is allowed. - * @param inputType - * : Type of input to accept (Integer, Double, etc...) - * @param aMinVal - * : Minimum value to accept - * @param aMaxVal - * : Maximum value to accept + * Object used to format programmatic entered values. Note aUnitProvider will also be used to determine if + * Floating or only Integral input is allowed. + * @param aMinMaxRange + * The range of values to accept. */ - public GNumberField(ActionListener aListener, UnitProvider aUnitProvider, double aMinVal, double aMaxVal) + public GNumberField(ActionListener aListener, UnitProvider aUnitProvider, Range aMinMaxRange) { super("", 0); + refFormat = null; refUnitProvider = aUnitProvider; - currValue = 0; - minValue = aMinVal; - maxValue = aMaxVal; + + minMaxRange = aMinMaxRange; + currValue = Double.NaN; isMutating = false; - failColor = Color.RED.darker(); - passColor = getForeground(); + colorFail = Color.RED.darker(); + colorPass = getForeground(); // Register the ActionListener if (aListener != null) @@ -69,31 +99,82 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi installUnit(); } - public GNumberField(ActionListener aListener, Unit aUnit, double aMinVal, double aMaxVal) + /** + * Standard Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aFormat + * NumberFormat used to transform the numerical value to a string. + * @param aMinMaxRange + * The range of values to accept. + */ + public GNumberField(ActionListener aListener, NumberFormat aFormat, Range aMinMaxRange) { - this(aListener, new ConstUnitProvider(aUnit), aMinVal, aMaxVal); + super("", 0); + + refFormat = aFormat; + refUnitProvider = null; + + minMaxRange = aMinMaxRange; + currValue = Double.NaN; + isMutating = false; + + colorFail = Color.RED.darker(); + colorPass = getForeground(); + + // Form the appropriate Document and initialize + myDocument = new NumberDocument(this, false); + super.setDocument(myDocument); + + // Register the ActionListener + if (aListener != null) + addActionListener(aListener); + + // Register for events of interest + myDocument.addDocumentListener(this); + + // Force the UI component to reflect the currValue. Note this is done last since this method + // assumes the GNumberField is already registered with myDocument. + forceTF(currValue); } /** - * Returns whether the current input is valid + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aUnit + * @param aMinMaxRange + * The range of values to accept. */ - public boolean isValidInput() + public GNumberField(ActionListener aListener, Unit aUnit, Range aMinMaxRange) { - Unit aUnit; - double modelVal; + this(aListener, new ConstUnitProvider(aUnit), aMinMaxRange); + } - // Ensure we have valid input - aUnit = refUnitProvider.getUnit(); + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aFormat + * NumberFormat used to transform the numerical value to a string. + */ + public GNumberField(ActionListener aListener, NumberFormat aFormat) + { + this(aListener, aFormat, Range.closed(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); + } - modelVal = aUnit.parseString(this.getText(), Double.NaN); - if (Double.isNaN(modelVal) == true) - return false; - - // Ensure the value is within range - if (modelVal < minValue || modelVal > maxValue) - return false; - - return true; + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + */ + public GNumberField(ActionListener aListener) + { + this(aListener, new DecimalFormat("#.###"), Range.closed(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY)); } /** @@ -110,6 +191,30 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi myDocument.addDocumentListener(this); } + /** + * Returns the {@link Color} used to change foreground text whenever invalid input is entered. + */ + public Color getColorFail() + { + return colorFail; + } + + /** + * Returns the {@link Color} used to change foreground text whenever valid input is entered. + */ + public Color getColorPass() + { + return colorPass; + } + + /** + * Returns the range of valid values. + */ + public Range getMinMaxRange() + { + return minMaxRange; + } + /** * Returns the currently stored model value */ @@ -120,20 +225,65 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi /** * Returns the currently stored model value as an integer. If the modelValue is NaN, then errorVal will be returned. - * The values MaxInt, MinInt are returned for Infinity. + * The values MAX_VALUE will be returned for +Infinity. The value MIN_VALUE will be returned for -Infinity. */ - public int getValueAsInt(int errorVal) + public long getValueAsLong(long aErrorVal) { if (Double.isNaN(currValue) == true) - return errorVal; + return aErrorVal; - return (int)currValue; + return (long) currValue; + } + + /** + * Returns the currently stored model value as an integer. If the modelValue is NaN, then errorVal will be returned. + * The values MAX_VALUE will be returned for +Infinity. The value MIN_VALUE will be returned for -Infinity. + */ + public int getValueAsInt(int aErrorVal) + { + if (Double.isNaN(currValue) == true) + return aErrorVal; + + return (int) currValue; + } + + /** + * Returns whether the current input is valid + */ + public boolean isValidInput() + { + // Ensure we have valid input + double modelVal = transformToModel(this.getText()); + if (Double.isNaN(modelVal) == true) + return false; + + // Ensure the value is within range + if (minMaxRange.contains(modelVal) == false) + return false; + + return true; + } + + /** + * Sets the {@link Color} used to indicate invalid input is entered. + */ + public void setColorFail(Color aColor) + { + colorFail = aColor; + } + + /** + * Sets the {@link Color} used to indicate valid input is entered. + */ + public void setColorPass(Color aColor) + { + colorPass = aColor; } /** * Takes in a model value and will display it with respect to the active unit. This method will not trigger an * ActionEvent. - *

+ *

* Note this method will do nothing if the UI is being "mutated" when this method is called. */ public void setValue(final double aValue) @@ -143,6 +293,16 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi if (isMutating == true) return; + // Bail if the value has not changed. We do this so that user + // entered input will not change if the model value has not changed. + double ulp = Math.ulp(aValue); + boolean ignoreInput = true; + ignoreInput &= Double.isNaN(ulp) == false; + ignoreInput &= Double.isFinite(ulp) == true; + ignoreInput &= Math.abs(currValue - aValue) < ulp; + if (ignoreInput == true) + return; + // Simple edit if we are not currently being mutated forceTF(aValue); updateGui(); @@ -151,28 +311,26 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi /** * Changes the range of acceptable values (in model units). Note the current value will be force to fit this range. */ - public void setMinMaxValue(double aMinValue, double aMaxValue) + public void setMinMaxRange(Range aMinMaxRange) { - Unit aUnit; - - minValue = aMinValue; - maxValue = aMaxValue; - if (currValue < minValue || currValue > maxValue) - currValue = minValue; + minMaxRange = aMinMaxRange; + if (minMaxRange.hasLowerBound() == true && currValue < minMaxRange.lowerEndpoint()) + currValue = minMaxRange.lowerEndpoint(); + else if (minMaxRange.hasUpperBound() == true && currValue > minMaxRange.upperEndpoint()) + currValue = minMaxRange.upperEndpoint(); // Update our document - aUnit = refUnitProvider.getUnit(); - myDocument.setMinMaxValue(aUnit.toUnit(minValue), aUnit.toUnit(maxValue)); - } - - @Override - public void setDocument(Document aDoc) - { -// throw new UnsupportedOperationException(); - if (aDoc != null) - aDoc.addDocumentListener(this); - - super.setDocument(aDoc); + double minValue = minMaxRange.lowerEndpoint(); + double maxValue = minMaxRange.upperEndpoint(); + if (refUnitProvider != null) + { + Unit tmpUnit = refUnitProvider.getUnit(); + myDocument.setMinMaxValue(tmpUnit.toUnit(minValue), tmpUnit.toUnit(maxValue)); + } + else + { + myDocument.setMinMaxValue(minValue, maxValue); + } } @Override @@ -193,6 +351,16 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi syncValue(aEvent); } + @Override + public void setDocument(Document aDoc) + { +// throw new UnsupportedOperationException(); + if (aDoc != null) + aDoc.addDocumentListener(this); + + super.setDocument(aDoc); + } + @Override public void unitChanged(UnitProvider aProvider, String aKey) { @@ -204,14 +372,11 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi */ protected void forceTF(double aValue) { - Unit aUnit; - String aStr; - // Save off the new model value, and check the validity currValue = aValue; - if (currValue < minValue || currValue > maxValue) + if (minMaxRange.contains(currValue) == false) currValue = Double.NaN; -// throw new RuntimeException("Programatic input is invalid. Is unit compatible? Input: " + aValue); +// throw new RuntimeException("Programmatic input is invalid. Is unit compatible? Input: " + aValue); // Invalid values shall just clear the text field and bail if (Double.isNaN(currValue) == true) @@ -220,13 +385,12 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi return; } - // Convert from model value to (unit) textual format - aUnit = refUnitProvider.getUnit(); - aStr = aUnit.getString(currValue); + // Convert from model value to text + String tmpStr = transformToString(currValue); // Update the GUI internals myDocument.removeDocumentListener(this); - setText(aStr); + setText(tmpStr); setCaretPosition(0); myDocument.addDocumentListener(this); } @@ -236,20 +400,17 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi */ protected void installUnit() { - Unit aUnit; - boolean aBool; - // Ensure that we have a valid Unit - aUnit = refUnitProvider.getUnit(); - if (aUnit instanceof NumberUnit == false) - throw new RuntimeException("refUnitProvider must return a Unit of type NumberUnit. Unit: " + aUnit); + Unit tmpUnit = refUnitProvider.getUnit(); + if (tmpUnit instanceof NumberUnit == false) + throw new RuntimeException("refUnitProvider must return a Unit of type NumberUnit. Unit: " + tmpUnit); // Update our Document to reflect whether this Unit supports floating point numbers - aBool = (aUnit instanceof NumberUnit) && (((NumberUnit)aUnit).isFloating() == true); - myDocument.setAllowFloats(aBool); + boolean tmpBool = (tmpUnit instanceof NumberUnit) && (((NumberUnit) tmpUnit).isFloating() == true); + myDocument.setAllowFloats(tmpBool); // Update the Document's MinMax values reflect the new Unit - setMinMaxValue(minValue, maxValue); + setMinMaxRange(minMaxRange); // Force myDocument's text to match the new unit forceTF(currValue); @@ -258,19 +419,16 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi /** * Keeps the "model" value conceptually linked to the GUI component. It will also trigger the actionEventListeners. */ - protected void syncValue(DocumentEvent e) + protected void syncValue(DocumentEvent aEvent) { - Unit aUnit; - // Mark ourself as mutating isMutating = true; - // Convert the textual (unit) value to the model value - aUnit = refUnitProvider.getUnit(); - currValue = aUnit.parseString(this.getText(), Double.NaN); + // Convert the string to the model value + currValue = transformToModel(this.getText()); // If the value is not in range then, it is invalid - if (currValue < minValue || currValue > maxValue) + if (minMaxRange.contains(currValue) == false) currValue = Double.NaN; // Notify our listeners and update the GUI @@ -286,13 +444,55 @@ public class GNumberField extends JTextField implements DocumentListener, UnitLi */ protected void updateGui() { - Color aColor; - - aColor = passColor; + Color tmpColor = colorPass; if (isValidInput() == false) - aColor = failColor; + tmpColor = colorFail; - setForeground(aColor); + setForeground(tmpColor); + } + + /** + * Helper method that will take a given value and convert it to a string. + * + * @param aValue + */ + private String transformToString(double aValue) + { + // Convert from model value to (unit) textual format + if (refUnitProvider != null) + { + Unit tmpUnit = refUnitProvider.getUnit(); + String tmpStr = tmpUnit.getString(aValue); + return tmpStr; + } + + return refFormat.format(aValue); + } + + /** + * Helper method that will take a String and convert it to the equivalent numerical value. On failure Double.NaN will + * be returned. + * + * @param aValue + */ + private double transformToModel(String aStr) + { + // Convert the textual (unit) value to the model value + if (refUnitProvider != null) + { + Unit tmpUnit = refUnitProvider.getUnit(); + double retValue = tmpUnit.parseString(aStr, Double.NaN); + return retValue; + } + + try + { + return Double.parseDouble(aStr); + } + catch (NumberFormatException aExp) + { + return Double.NaN; + } } } diff --git a/src/glum/gui/component/GNumberFieldSlider.java b/src/glum/gui/component/GNumberFieldSlider.java new file mode 100644 index 0000000..c1b8eb4 --- /dev/null +++ b/src/glum/gui/component/GNumberFieldSlider.java @@ -0,0 +1,218 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.component; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.text.NumberFormat; + +import javax.swing.JPanel; + +import com.google.common.collect.Range; + +import glum.gui.GuiUtil; +import net.miginfocom.swing.MigLayout; + +/** + * User interface component that combines a {@link GNumberField} and a {@link GSlider} into a single unified component. + * + * @author lopeznr1 + */ +public class GNumberFieldSlider extends JPanel implements ActionListener +{ + // Ref vars + private final ActionListener refLister; + + // Gui vars + private GNumberField valueNF; + private GSlider valueS; + + /** + * Standard Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aFormat + * NumberFormat used to transform the numerical value to a string. + * @param aMinMaxRange + * The range of values to accept. + * @param aNumColumns + * The number of columns associated with the {@link GNumberField}. + */ + public GNumberFieldSlider(ActionListener aListener, NumberFormat aFormat, Range aMinMaxRange, + int aNumColumns) + { + refLister = aListener; + + buildGui(aFormat, aMinMaxRange); + + setNumColumns(aNumColumns); + setNumSteps(100); + } + + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aFormat + * NumberFormat used to transform the numerical value to a string. + * @param aMinMaxRange + * The range of values to accept. + */ + public GNumberFieldSlider(ActionListener aListener, NumberFormat aFormat, Range aMinMaxRange) + { + this(aListener, aFormat, aMinMaxRange, 4); + } + + /** + * Returns whether the current input is valid + */ + public boolean isValidInput() + { + // Delegate + return valueNF.isValidInput(); + } + + /** + * Returns the selected value. + */ + public double getValue() + { + return valueNF.getValue(); + } + + /** + * Returns the currently stored model value as an integer. + *

+ * See {@link GNumberField#getValueAsInt(int)} + */ + public int getValueAsInt(int aErrorVal) + { + return valueNF.getValueAsInt(aErrorVal); + } + + /** + * Returns true if the value is being actively adjusted. + *

+ * See also {@link GSlider#getValueIsAdjusting()} + */ + public boolean getValueIsAdjusting() + { + // Delegate + return valueS.getValueIsAdjusting(); + } + + /** + * Sets in the selected value. Note no events will be fired. + */ + public void setValue(double aVal) + { + valueNF.setValue(aVal); + valueS.setModelValue(aVal); + } + + /** + * Set the editable state of the UI component. + */ + public void setEditable(boolean aBool) + { + valueNF.setEditable(aBool); + valueS.setEnabled(aBool); + } + + /** + * Set the enable state of the UI component. + */ + @Override + public void setEnabled(boolean aBool) + { + GuiUtil.setEnabled(this, aBool); + } + + /** + * Sets in the steps to be an integral value. + *

+ * The minVal and maxVal must be integers otherwise this method will throw an exception. The number of steps will be: + * (maxVal - minVal). + */ + public void setIntegralSteps() + { + var tmpMinMaxRange = valueNF.getMinMaxRange(); + double tmpMinVal = tmpMinMaxRange.lowerEndpoint(); + double tmpMaxVal = tmpMinMaxRange.upperEndpoint(); + + int intMaxVal = (int) tmpMaxVal; + int intMinVal = (int) tmpMinVal; + + var isIntegral = true; + isIntegral &= tmpMinVal - intMinVal == 0; + isIntegral &= tmpMaxVal - intMaxVal == 0; + if (isIntegral == false) + throw new RuntimeException("Min,Max values are not integral: [" + tmpMinVal + ", " + tmpMaxVal + "]"); + + // Delegate + int numSteps = intMaxVal - intMinVal; + setNumSteps(numSteps); + } + + /** + * Sets in the number of columns for the associated GTextField. + */ + public void setNumColumns(int aNumColumns) + { + valueNF.setColumns(aNumColumns); + } + + /** + * Sets in the number of steps associated with the slider. + */ + public void setNumSteps(int aNumSteps) + { + valueS.setNumSteps(aNumSteps); + } + + @Override + public void actionPerformed(ActionEvent aEvent) + { + var source = aEvent.getSource(); + if (source == valueNF) + { + double tmpVal = valueNF.getValue(); + valueS.setModelValue(tmpVal); + } + else if (source == valueS) + { + double tmpVal = valueS.getModelValue(); + valueNF.setValue(tmpVal); + } + + var tmpEvent = new ActionEvent(this, 0, ""); + refLister.actionPerformed(tmpEvent); + } + + /** + * Helper method that builds the unified GUI + */ + private void buildGui(NumberFormat aNumberFormat, Range aMinMaxRange) + { + valueNF = new GNumberField(this, aNumberFormat, aMinMaxRange); + valueS = new GSlider(this, aMinMaxRange); + + setLayout(new MigLayout("", "0[]0", "0[]0")); + add(valueNF, "w 40:"); + add(valueS, "growx,pushx"); + } + +} diff --git a/src/glum/gui/component/GPasswordField.java b/src/glum/gui/component/GPasswordField.java index 2d172e9..66a7dc1 100644 --- a/src/glum/gui/component/GPasswordField.java +++ b/src/glum/gui/component/GPasswordField.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.event.ActionListener; diff --git a/src/glum/gui/component/GSlider.java b/src/glum/gui/component/GSlider.java index 2299e86..dcf634e 100644 --- a/src/glum/gui/component/GSlider.java +++ b/src/glum/gui/component/GSlider.java @@ -1,46 +1,91 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; + import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import com.google.common.collect.Range; + +/** + * User interface input used to capture an individual numerical input (type: double). + *

+ * Unlike JSlider, users of this class should not use getValue() / setValue() but rather getModelValue() / + * setModelValue() methods. Also it should not be necessary to register ChangeListeners - rather an ActionListener + * should be sufficient. + * + * @author lopeznr1 + */ public class GSlider extends JSlider implements ChangeListener { - private ActionListener myListener; - private double minVal, maxVal, rngVal; + // Attributes + private final ActionListener refListener; + + // State vars + private Range minMaxRange; private int maxSteps; - public GSlider(ActionListener aListener, int aMaxSteps, double aMinVal, double aMaxVal) + /** + * Standard Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aMinMaxRange + * The range of values to accept. + * @param aMaxSteps + * The number of steps associated with the slider. + */ + public GSlider(ActionListener aListener, Range aMinMaxRange, int aMaxSteps) { super(0, aMaxSteps); addChangeListener(this); - myListener = aListener; + refListener = aListener; + minMaxRange = aMinMaxRange; maxSteps = aMaxSteps; - - minVal = aMinVal; - maxVal = aMaxVal; - rngVal = maxVal - minVal; } - public GSlider(ActionListener aListener, double aMinVal, double aMaxVal) + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aMinMaxRange + * The range of values to accept. + */ + public GSlider(ActionListener aListener, Range aMinMaxRange) { - this(aListener, 1000, aMinVal, aMaxVal); + this(aListener, aMinMaxRange, 1000); } /** * Returns the model value for which this slider is currently set to. - *

+ *

* Use this method over {@link JSlider#getValue()} */ public double getModelValue() { - double retVal; + double minVal = minMaxRange.lowerEndpoint(); + double maxVal = minMaxRange.upperEndpoint(); + double rngVal = maxVal - minVal; - retVal = minVal + ((super.getValue() / (double)maxSteps) * rngVal); + double retVal = minVal + ((super.getValue() / (double) maxSteps) * rngVal); return retVal; } @@ -48,11 +93,13 @@ public class GSlider extends JSlider implements ChangeListener * Takes in the model's minVal and maxVal range. The current chosen model value will be adjusted to be in the middle * of the range. */ - public void setModelRange(double aMinVal, double aMaxVal) + public void setModelRange(Range aMinMaxRange) { - minVal = aMinVal; - maxVal = aMaxVal; - rngVal = maxVal - minVal; + minMaxRange = aMinMaxRange; + + double minVal = minMaxRange.lowerEndpoint(); + double maxVal = minMaxRange.upperEndpoint(); + double rngVal = maxVal - minVal; setModelValue(minVal + rngVal / 2); } @@ -60,17 +107,37 @@ public class GSlider extends JSlider implements ChangeListener /** * Takes in a model value and will adjust the slider to display the value. Note this method will not trigger an * ActionEvent. - *

+ *

* Use this method over {@link JSlider#setValue} */ public void setModelValue(double aVal) { - double guiVal; - - guiVal = ((aVal - minVal) / rngVal) * maxSteps; + double minVal = minMaxRange.lowerEndpoint(); + double maxVal = minMaxRange.upperEndpoint(); + double rngVal = maxVal - minVal; removeChangeListener(this); - setValue((int)guiVal); + double guiVal = ((aVal - minVal) / rngVal) * maxSteps; + setValue((int) guiVal); + addChangeListener(this); + } + + /** + * Sets in the number of steps associated with the GSlider. + *

+ * Values will be uniformly distributed over the range / numSteps + * + * @param aNumSteps + * The number of steps the slider should have. + */ + public void setNumSteps(int aNumSteps) + { + removeChangeListener(this); + + setMinimum(0); + setMaximum(aNumSteps); + maxSteps = aNumSteps; + addChangeListener(this); } @@ -80,12 +147,26 @@ public class GSlider extends JSlider implements ChangeListener notifyLisener(); } + @Override + @Deprecated + public int getValue() + { + return super.getValue(); + } + + @Override + @Deprecated + public void setValue(int n) + { + super.setValue(n); + } + /** * Helper method to notify our listener */ private void notifyLisener() { - myListener.actionPerformed(new ActionEvent(this, 0, "update")); + refListener.actionPerformed(new ActionEvent(this, 0, "update")); } } diff --git a/src/glum/gui/component/GTextField.java b/src/glum/gui/component/GTextField.java index 61e96b7..646c8fb 100644 --- a/src/glum/gui/component/GTextField.java +++ b/src/glum/gui/component/GTextField.java @@ -1,21 +1,49 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component; import java.awt.event.ActionListener; -import javax.swing.*; -import javax.swing.event.*; -import javax.swing.text.*; -public class GTextField extends JTextField implements DocumentListener +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.Document; +import javax.swing.text.PlainDocument; + +/** + * User interface input used to capture an input string. + *

+ * Unlike JTextField, users of this class should not use getText() / setText() but rather getValue() / setValue() + * methods. Also it should not be necessary to register DocumentListeners - rather an ActionListener should be + * sufficient. + * + * @author lopeznr1 + */ +public class GTextField extends GBaseTextField implements DocumentListener { /** - * Constructor - * + * Standard Constructor + * * @param aListener - * : Default ActionListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aLabel + * The text value to use as the initial input. + * @param aHint + * The hint to show when no input has been entered. */ - public GTextField(ActionListener aListener) + public GTextField(ActionListener aListener, String aLabel, String aHint) { - super("", 0); + super(aLabel, 0); if (aListener != null) addActionListener(aListener); @@ -27,6 +55,35 @@ public class GTextField extends JTextField implements DocumentListener // Register for events of interest doc.addDocumentListener(this); + + if (aHint != null) + setHint(aHint); + + forceTF(aLabel); + } + + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + * @param aLabel + * The text value to use as the initial input. + */ + public GTextField(ActionListener aListener, String aLabel) + { + this(aListener, aLabel, null); + } + + /** + * Simplified Constructor + * + * @param aListener + * An ActionListener that will be notified when ever the user makes any input changes. + */ + public GTextField(ActionListener aListener) + { + this(aListener, "", null); } /** diff --git a/src/glum/gui/component/GToggle.java b/src/glum/gui/component/GToggle.java deleted file mode 100644 index 5043c60..0000000 --- a/src/glum/gui/component/GToggle.java +++ /dev/null @@ -1,76 +0,0 @@ -package glum.gui.component; - -import javax.swing.Icon; -import javax.swing.JToggleButton; - -public class GToggle extends JToggleButton -{ - // State vars - protected boolean isActive; - - // Gui vars - protected Icon falseIcon, trueIcon; - - public GToggle(Icon aFalseIcon, Icon aTrueIcon, boolean aIsActive) - { - super(); - - falseIcon = aFalseIcon; - trueIcon = aTrueIcon; - setSelected(aIsActive); - - setModel(new GToggleButtonModel()); - } - - @Override - public void setSelected(boolean b) - { - super.setSelected(b); - updateGui(); - } - - @Override - public void doClick(int pressTime) - { - super.doClick(pressTime); - updateGui(); - } - - /** - * Utility method - */ - private void updateGui() - { - if (isSelected() == true) - setIcon(trueIcon); - else - setIcon(falseIcon); - } - - /** - * The ToggleButton model - *

- * Warning: Serialized objects of this class will not be compatible with future Swing releases. The - * current serialization support is appropriate for short term storage or RMI between applications running the same - * version of Swing. As of 1.4, support for long term storage of all JavaBeansTM - * has been added to the java.beans package. Please see {@link java.beans.XMLEncoder}. - */ - public class GToggleButtonModel extends ToggleButtonModel - { - /** - * Creates a new ToggleButton Model - */ - public GToggleButtonModel() - { - } - - @Override - public void setSelected(boolean b) - { - super.setSelected(b); - - updateGui(); - } - } - -} diff --git a/src/glum/gui/component/banner/Banner.java b/src/glum/gui/component/banner/Banner.java index 566cac7..c33e21e 100644 --- a/src/glum/gui/component/banner/Banner.java +++ b/src/glum/gui/component/banner/Banner.java @@ -1,11 +1,25 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component.banner; import java.awt.*; -import java.awt.geom.*; -import java.util.*; -import javax.swing.*; +import java.awt.geom.Rectangle2D; +import java.util.Collection; -import glum.gui.*; +import javax.swing.JComponent; + +import glum.io.ParseUtil; import glum.io.token.MatchTokenizer; import glum.io.token.Tokenizer; @@ -37,7 +51,7 @@ public class Banner extends JComponent double sX, sY, offSetY; super.paintComponent(g); - g2d = (Graphics2D)g; + g2d = (Graphics2D) g; // Determine the window boundaries winW = getWidth(); @@ -72,7 +86,7 @@ public class Banner extends JComponent sY = winH / 2.0 - msgH / 2.0; g2d.setFont(myConfig.font); g2d.setColor(myConfig.fgColor); - g2d.drawString(displayMsg, (int)sX, (int)(sY + offSetY)); + g2d.drawString(displayMsg, (int) sX, (int) (sY + offSetY)); } /** @@ -102,8 +116,8 @@ public class Banner extends JComponent } /** - * Utility method that converts a string to a BannerConfig. Eventually this method class should be - * moved to a utility class of sorts. + * Utility method that converts a string to a BannerConfig. Eventually this method class should be moved to a utility + * class of sorts. */ public static BannerConfig readBannerConfig(String strLine) { @@ -128,7 +142,7 @@ public class Banner extends JComponent for (String aInstr : instrSet) { parmSet = parmTokenizer.getTokens(aInstr); - parms = parmSet.toArray(new String[] {""}); + parms = parmSet.toArray(new String[] { "" }); if (parms.length == 0) { @@ -152,15 +166,15 @@ public class Banner extends JComponent size = 12; if (parms.length > 2) - size = GuiUtil.readInt(parms[2], 12); + size = ParseUtil.readInt(parms[2], 12); bold = false; if (parms.length > 3) - bold = GuiUtil.readBoolean(parms[3], false); + bold = ParseUtil.readBoolean(parms[3], false); italic = false; if (parms.length > 4) - italic = GuiUtil.readBoolean(parms[4], false); + italic = ParseUtil.readBoolean(parms[4], false); style = 0; if (bold == false && italic == false) @@ -176,22 +190,22 @@ public class Banner extends JComponent { int r, g, b; - r = GuiUtil.readRangeInt(parms[1], 0, 255, 255); - g = GuiUtil.readRangeInt(parms[2], 0, 255, 255); - b = GuiUtil.readRangeInt(parms[3], 0, 255, 255); + r = ParseUtil.readRangeInt(parms[1], 0, 255, 255); + g = ParseUtil.readRangeInt(parms[2], 0, 255, 255); + b = ParseUtil.readRangeInt(parms[3], 0, 255, 255); aConfig.fgColor = new Color(r, g, b); } else if (parms[0].equalsIgnoreCase("bgColor") == true && parms.length >= 4) { int r, g, b, a; - r = GuiUtil.readRangeInt(parms[1], 0, 255, 255); - g = GuiUtil.readRangeInt(parms[2], 0, 255, 255); - b = GuiUtil.readRangeInt(parms[3], 0, 255, 255); + r = ParseUtil.readRangeInt(parms[1], 0, 255, 255); + g = ParseUtil.readRangeInt(parms[2], 0, 255, 255); + b = ParseUtil.readRangeInt(parms[3], 0, 255, 255); a = 255; if (parms.length > 4) - a = GuiUtil.readRangeInt(parms[4], 0, 255, 255); + a = ParseUtil.readRangeInt(parms[4], 0, 255, 255); aConfig.bgColor = new Color(r, g, b, a); } @@ -199,20 +213,20 @@ public class Banner extends JComponent { int r, g, b; - r = GuiUtil.readRangeInt(parms[1], 0, 255, 255); - g = GuiUtil.readRangeInt(parms[2], 0, 255, 255); - b = GuiUtil.readRangeInt(parms[3], 0, 255, 255); + r = ParseUtil.readRangeInt(parms[1], 0, 255, 255); + g = ParseUtil.readRangeInt(parms[2], 0, 255, 255); + b = ParseUtil.readRangeInt(parms[3], 0, 255, 255); aConfig.borderColor = new Color(r, g, b); if (parms.length > 4) - aConfig.borderWidth = GuiUtil.readRangeInt(parms[4], 0, 10, 0); + aConfig.borderWidth = ParseUtil.readRangeInt(parms[4], 0, 10, 0); if (parms.length > 5) - aConfig.borderPad = GuiUtil.readRangeInt(parms[5], -20, 20, 0); + aConfig.borderPad = ParseUtil.readRangeInt(parms[5], -20, 20, 0); } else if (parms[0].equalsIgnoreCase("repeatMsg") == true && parms.length == 2) { - aConfig.numRepeats = GuiUtil.readRangeInt(parms[1], -1, 100, 0); + aConfig.numRepeats = ParseUtil.readRangeInt(parms[1], -1, 100, 0); } } diff --git a/src/glum/gui/component/banner/BannerConfig.java b/src/glum/gui/component/banner/BannerConfig.java index 11a8a4d..ebcda7b 100644 --- a/src/glum/gui/component/banner/BannerConfig.java +++ b/src/glum/gui/component/banner/BannerConfig.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component.banner; import java.awt.*; diff --git a/src/glum/gui/component/model/GComboBoxModel.java b/src/glum/gui/component/model/GComboBoxModel.java index a35352e..deefa41 100644 --- a/src/glum/gui/component/model/GComboBoxModel.java +++ b/src/glum/gui/component/model/GComboBoxModel.java @@ -1,26 +1,51 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component.model; import java.util.Collection; + import javax.swing.ComboBoxModel; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; +import glum.gui.component.GComboBox; + +/** + * Implementation of {@link ComboBoxModel} that is used with {@link GComboBox}. + * + * @author lopeznr1 + */ public class GComboBoxModel extends GListModel implements ComboBoxModel { - protected G1 chosenItem; + private G1 chosenItem; - public GComboBoxModel(G1... aItemArr) + /** + * Standard Constructor + */ + public GComboBoxModel(Collection aItemC) { - this(Lists.newArrayList(aItemArr)); - } - - public GComboBoxModel(Collection aItemList) - { - super(aItemList); + super(aItemC); chosenItem = null; - if (itemList.size() > 0) - chosenItem = itemList.get(0); + if (itemL.size() > 0) + chosenItem = itemL.get(0); + } + + @SafeVarargs + public GComboBoxModel(G1... aItemArr) + { + this(ImmutableList.copyOf(aItemArr)); } @Override @@ -38,17 +63,18 @@ public class GComboBoxModel extends GListModel implements ComboBoxModel< super.removeItem(aItem); chosenItem = null; - if (itemList.size() > 0) - chosenItem = itemList.get(0); + if (itemL.size() > 0) + chosenItem = itemL.get(0); } /** * Note aItem must be of the Generified type */ - @Override @SuppressWarnings("unchecked") + @Override + @SuppressWarnings("unchecked") public void setSelectedItem(Object aItem) { - chosenItem = (G1)aItem; + chosenItem = (G1) aItem; } @Override diff --git a/src/glum/gui/component/model/GListModel.java b/src/glum/gui/component/model/GListModel.java index 9a44ee9..555847f 100644 --- a/src/glum/gui/component/model/GListModel.java +++ b/src/glum/gui/component/model/GListModel.java @@ -1,79 +1,98 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.component.model; -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; +import java.util.*; import javax.swing.AbstractListModel; -import com.google.common.collect.Lists; - /** * Generified mutable ListModel + * + * @author lopeznr1 */ public class GListModel extends AbstractListModel { - protected List itemList; + protected final List itemL; - public GListModel(Collection aItemList) + /** + * Standard Constructor + */ + public GListModel(Collection aItemC) { - itemList = Lists.newArrayList(aItemList); + itemL = new ArrayList<>(aItemC); } -// public GListModel(G1... aItemArr) -// { -// itemList = Lists.newArrayList(aItemArr); -// } -// /** * Adds aItem to this model */ public void addItem(G1 aItem) { - int index; - - itemList.add(aItem); - - index = itemList.size() - 1; + itemL.add(aItem); + + var index = itemL.size() - 1; fireIntervalAdded(this, index, index); } - + /** * Removes aItem from this model */ public void removeItem(G1 aItem) { - int index; - - index = itemList.indexOf(aItem); - itemList.remove(aItem); - + var index = itemL.indexOf(aItem); + itemL.remove(aItem); + fireIntervalRemoved(this, index, index); } - + /** * Removes all the items from this model */ public void removeAllItems() { - int lastIndex; - // Bail if the list is empty - if (itemList.isEmpty() == true) + if (itemL.isEmpty() == true) return; - - lastIndex = itemList.size() - 1; - itemList.clear(); - + + var lastIndex = itemL.size() - 1; + itemL.clear(); + fireIntervalRemoved(this, 0, lastIndex); } - + + /** + * Replaces the original item with the provided replacement. + *

+ * Throws an exception if the original item is not installed. + */ + public void replaceItem(G1 aOrigItem, G1 aReplItem) + { + int tmpIdx = itemL.indexOf(aOrigItem); + if (tmpIdx == -1) + throw new RuntimeException("The original item is not in the list"); + + itemL.set(tmpIdx, aReplItem); + + fireContentsChanged(this, tmpIdx, tmpIdx); + } + /** * Returns a list of all the items */ - public ArrayList getAllItems() + public List getAllItems() { - return Lists.newArrayList(itemList); + return new ArrayList<>(itemL); } /** @@ -81,18 +100,18 @@ public class GListModel extends AbstractListModel */ public int indexOf(G1 aItem) { - return itemList.indexOf(aItem); + return itemL.indexOf(aItem); } @Override public int getSize() { - return itemList.size(); + return itemL.size(); } @Override public G1 getElementAt(int index) { - return itemList.get(index); + return itemL.get(index); } } diff --git a/src/glum/gui/dnd/PlainTransferHandler.java b/src/glum/gui/dnd/PlainTransferHandler.java index 38d7ec9..11f3a2f 100644 --- a/src/glum/gui/dnd/PlainTransferHandler.java +++ b/src/glum/gui/dnd/PlainTransferHandler.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dnd; import java.awt.datatransfer.Transferable; @@ -6,19 +19,20 @@ import javax.swing.JComponent; import javax.swing.TransferHandler; /** - * Generic TransferHandler that supports transferring an arbitrary Transferable. Note that before the DnD transfer - * mechanism is triggered via the method exportAsDrag(), the developer must set in the Transferable via the method - * setWorkTransferable(). + * Generic {@link TransferHandler} that supports transferring an arbitrary {@link Transferable}. Note that before the + * DnD transfer mechanism is triggered via the method exportAsDrag(), the developer must set in the {@link Transferable} + * via the method {@link #setWorkTransferable(Transferable)}. + * + * @author lopeznr1 */ public class PlainTransferHandler extends TransferHandler { // State vars protected Transferable workTransferable; + /** Standard Constructor */ public PlainTransferHandler() { - super(); - workTransferable = null; } diff --git a/src/glum/gui/dock/BaseDockable.java b/src/glum/gui/dock/BaseDockable.java index 6d7e266..ddd183d 100644 --- a/src/glum/gui/dock/BaseDockable.java +++ b/src/glum/gui/dock/BaseDockable.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import javax.swing.Icon; @@ -11,18 +24,16 @@ public class BaseDockable extends DefaultDockable // Tells whether this Dockable can be dragged and dropped to another station private DockStation homeStation; private boolean isTransferable; - + public BaseDockable() { - super(); - isTransferable = true; } - + public BaseDockable(JComponent aComp, String aTitle, Icon aIcon) { super(aComp, aTitle, aIcon); - + isTransferable = true; } @@ -30,21 +41,19 @@ public class BaseDockable extends DefaultDockable { if (isTransferable == true) return true; - + // We can only be transfered to our homeStation when we are not transferable return aStation == homeStation; } - + public void setTransferable(boolean aBool) { homeStation = null; isTransferable = aBool; - - + // Record our parent when we become non transferable if (isTransferable == false) homeStation = getDockParent(); } - } diff --git a/src/glum/gui/dock/CloseableDockable.java b/src/glum/gui/dock/CloseableDockable.java index ff99df5..10f5a71 100644 --- a/src/glum/gui/dock/CloseableDockable.java +++ b/src/glum/gui/dock/CloseableDockable.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import glum.gui.dock.action.Closeable; diff --git a/src/glum/gui/dock/CustomPlaceholderStrategy.java b/src/glum/gui/dock/CustomPlaceholderStrategy.java index eb1ab6e..873f802 100644 --- a/src/glum/gui/dock/CustomPlaceholderStrategy.java +++ b/src/glum/gui/dock/CustomPlaceholderStrategy.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import bibliothek.gui.DockStation; diff --git a/src/glum/gui/dock/DockUtil.java b/src/glum/gui/dock/DockUtil.java index a0f4445..e87e881 100644 --- a/src/glum/gui/dock/DockUtil.java +++ b/src/glum/gui/dock/DockUtil.java @@ -1,14 +1,26 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; import javax.swing.Icon; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - import bibliothek.gui.DockController; import bibliothek.gui.DockFrontend; import bibliothek.gui.DockStation; @@ -20,6 +32,11 @@ import bibliothek.gui.dock.action.actions.SimpleButtonAction; import bibliothek.gui.dock.station.screen.ScreenFullscreenAction; import bibliothek.gui.dock.station.split.SplitFullScreenAction; +/** + * Collection of utility methods useful for working dockables / dock stations. + * + * @author lopeznr1 + */ public class DockUtil { /** @@ -27,14 +44,14 @@ public class DockUtil */ public static SimpleButtonAction createAction(String aText, Icon aIcon, ActionListener aListener) { - SimpleButtonAction aAction; + SimpleButtonAction retAction; - aAction = new SimpleButtonAction(); - aAction.setText(aText); - aAction.setIcon(aIcon); - aAction.addActionListener(aListener); + retAction = new SimpleButtonAction(); + retAction.setText(aText); + retAction.setIcon(aIcon); + retAction.addActionListener(aListener); - return aAction; + return retAction; } /** @@ -44,24 +61,24 @@ public class DockUtil */ public static DockAction createFullScreenAction(DockStation aStation, DockController aController) { - DockAction fullScreenAction; + DockAction retAction; if (aStation instanceof SplitDockStation) { - fullScreenAction = new SplitFullScreenAction((SplitDockStation)aStation); - ((SplitFullScreenAction)fullScreenAction).setController(aController); + retAction = new SplitFullScreenAction((SplitDockStation) aStation); + ((SplitFullScreenAction) retAction).setController(aController); } else if (aStation instanceof ScreenDockStation) { - fullScreenAction = new ScreenFullscreenAction((ScreenDockStation)aStation); - ((ScreenFullscreenAction)fullScreenAction).setController(aController); + retAction = new ScreenFullscreenAction((ScreenDockStation) aStation); + ((ScreenFullscreenAction) retAction).setController(aController); } else { throw new RuntimeException("Unsupported Dockable type: " + aStation); } - return fullScreenAction; + return retAction; } /** @@ -75,7 +92,7 @@ public class DockUtil { evalDockable = aStation.getDockable(c1); if (evalDockable instanceof DockStation) - evalDockable = findDockable((DockStation)evalDockable, aClass); + evalDockable = findDockable((DockStation) evalDockable, aClass); if (evalDockable.getClass() == aClass) return aClass.cast(evalDockable); @@ -90,19 +107,19 @@ public class DockUtil */ public static List findDockableList(DockFrontend aFrontend, Class... aClassArr) { - Set> classSet; - List itemList; + Set> tmpClassS; + List retItemL; // Transform the match class array to a set - classSet = Sets.newHashSet(); + tmpClassS = new HashSet<>(); for (Class aClass : aClassArr) - classSet.add(aClass); + tmpClassS.add(aClass); - itemList = Lists.newLinkedList(); + retItemL = new ArrayList<>(); for (DockStation aStation : aFrontend.getRoots()) - findDockableList(aStation, classSet, itemList); + findDockableList(aStation, tmpClassS, retItemL); - return itemList; + return retItemL; } /** @@ -111,18 +128,18 @@ public class DockUtil */ public static List findDockableList(DockFrontend aFrontend, Class aClass) { - Set> classSet; - List itemList; + Set> tmpClassS; + List retItemL; // Transform the match class array to a set - classSet = Sets.newHashSet(); - classSet.add(aClass); + tmpClassS = new HashSet<>(); + tmpClassS.add(aClass); - itemList = Lists.newLinkedList(); + retItemL = new ArrayList<>(); for (DockStation aStation : aFrontend.getRoots()) - findDockableList(aStation, classSet, itemList); + findDockableList(aStation, tmpClassS, retItemL); - return itemList; + return retItemL; } /** @@ -131,18 +148,18 @@ public class DockUtil */ public static List findDockableList(DockStation aStation, Class... aClassArr) { - Set> classSet; - List itemList; + Set> tmpClassS; + List retItemL; // Transform the match class array to a set - classSet = Sets.newHashSet(); + tmpClassS = new HashSet<>(); for (Class aClass : aClassArr) - classSet.add(aClass); + tmpClassS.add(aClass); - itemList = Lists.newLinkedList(); - findDockableList(aStation, classSet, itemList); + retItemL = new ArrayList<>(); + findDockableList(aStation, tmpClassS, retItemL); - return itemList; + return retItemL; } /** @@ -151,32 +168,32 @@ public class DockUtil */ public static List findDockableList(DockStation aStation, Class aClass) { - Set> classSet; - List itemList; + Set> tmpClassS; + List retItemL; // Transform the match class array to a set - classSet = Sets.newHashSet(); - classSet.add(aClass); + tmpClassS = new HashSet<>(); + tmpClassS.add(aClass); - itemList = Lists.newLinkedList(); - findDockableList(aStation, classSet, itemList); + retItemL = new ArrayList<>(); + findDockableList(aStation, tmpClassS, retItemL); - return itemList; + return retItemL; } /** * Helper method to remove all PlotGroupStations and ChartDockables */ - public static void removeAllDockablesOfType(DockFrontend aFrontend, Class... dockTypeArr) + public static void removeAllDockablesOfType(DockFrontend aFrontend, Class... aDockTypeArr) { - List dockList; + List dockL; DockStation dockStation; // Gather all of the Dockables of interest - dockList = DockUtil.findDockableList(aFrontend, dockTypeArr); + dockL = DockUtil.findDockableList(aFrontend, aDockTypeArr); // Remove all of the Dockables - for (Dockable aDock : dockList) + for (Dockable aDock : dockL) { dockStation = aDock.getDockParent(); if (dockStation != null) @@ -189,24 +206,25 @@ public class DockUtil * results will be stored in aItemList */ @SuppressWarnings("unchecked") - private static void findDockableList(DockStation aStation, Set> aClassSet, List aItemList) + private static void findDockableList(DockStation aStation, Set> aClassS, + List aItemL) { Dockable evalDockable; for (int c1 = 0; c1 < aStation.getDockableCount(); c1++) { evalDockable = aStation.getDockable(c1); - for (Class aClass : aClassSet) + for (Class aClass : aClassS) { if (aClass.isAssignableFrom(evalDockable.getClass()) == true) { - aItemList.add((G1)evalDockable); + aItemL.add((G1) evalDockable); break; } } if (evalDockable instanceof DockStation) - findDockableList((DockStation)evalDockable, aClassSet, aItemList); + findDockableList((DockStation) evalDockable, aClassS, aItemL); } } diff --git a/src/glum/gui/dock/FrontendAddConfigPanel.java b/src/glum/gui/dock/FrontendAddConfigPanel.java index 1d512e8..6727eda 100644 --- a/src/glum/gui/dock/FrontendAddConfigPanel.java +++ b/src/glum/gui/dock/FrontendAddConfigPanel.java @@ -1,23 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; -import java.awt.*; +import java.awt.Component; import java.awt.event.ActionEvent; import bibliothek.gui.DockFrontend; +import glum.gui.panel.generic.BaseTextInputPanel; -import glum.gui.panel.generic.TextInputPanel; - -public class FrontendAddConfigPanel extends TextInputPanel +/** + * UI component that allows the creation of a docking configuration. + * + * @author lopeznr1 + */ +public class FrontendAddConfigPanel extends BaseTextInputPanel { // Constants public static final String DEFAULT_NAME = "Default"; - // State vars - protected DockFrontend refFrontend; + // Ref vars + private final DockFrontend refFrontend; - /** - * Constructor - */ + /** Standard Constructor */ public FrontendAddConfigPanel(Component aParent, DockFrontend aFrontend) { super(aParent); @@ -32,14 +47,11 @@ public class FrontendAddConfigPanel extends TextInputPanel @Override public void actionPerformed(ActionEvent e) { - Object source; - String configName; - // Save of the configuration - source = e.getSource(); + var source = e.getSource(); if (source == acceptB) { - configName = getInput(); + var configName = getInput(); refFrontend.save(configName); } @@ -50,16 +62,14 @@ public class FrontendAddConfigPanel extends TextInputPanel @Override protected void updateGui() { - String inputStr, infoMsg; - boolean isEnabled; - // Assume the GUI is invalid - isEnabled = false; + var isEnabled = false; // Retrieve the name - inputStr = inputTF.getText(); + var inputStr = inputTF.getText(); // Determine the validity of specified name + String infoMsg; if (inputStr.equals("") == true) { infoMsg = "Please enter a valid configuration name."; diff --git a/src/glum/gui/dock/FrontendManageConfigPanel.java b/src/glum/gui/dock/FrontendManageConfigPanel.java index 3d6679a..b0c4927 100644 --- a/src/glum/gui/dock/FrontendManageConfigPanel.java +++ b/src/glum/gui/dock/FrontendManageConfigPanel.java @@ -1,21 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Collection; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.SwingUtilities; -import javax.swing.border.BevelBorder; +import java.util.ArrayList; + +import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -//import echo.gui.LookUp; - -import com.google.common.collect.Lists; - +import bibliothek.gui.DockFrontend; import glum.database.QueryItem; import glum.gui.FocusUtil; import glum.gui.GuiUtil; @@ -25,24 +33,27 @@ import glum.gui.panel.itemList.ItemListPanel; import glum.gui.panel.itemList.StaticItemProcessor; import glum.gui.panel.itemList.query.QueryComposer; import glum.gui.panel.itemList.query.QueryItemHandler; - -import bibliothek.gui.DockFrontend; - import net.miginfocom.swing.MigLayout; +/** + * UI component that provides a listing of all docking configurations. + * + * @author lopeznr1 + */ public class FrontendManageConfigPanel extends GlassPanel implements ActionListener, ListSelectionListener { // GUI vars - protected ItemListPanel listPanel; - protected JButton deleteB, closeB; + private ItemListPanel listPanel; + private JButton deleteB, closeB; // State vars - protected DockFrontend refFrontend; - protected StaticItemProcessor myItemProcessor; + private DockFrontend refFrontend; + private StaticItemProcessor myItemProcessor; /** - * Constructor - * @param aFrontend + * Standard Constructor + * + * @param aFrontend */ public FrontendManageConfigPanel(Component aParent, DockFrontend aFrontend) { @@ -53,8 +64,8 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe // Build the actual GUI buildGuiArea(); - setPreferredSize(new Dimension(250, 300));//TODO:getPreferredSize().height)); - + setPreferredSize(new Dimension(250, 300));// TODO:getPreferredSize().height)); + // Set up keyboard short cuts FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(closeB)); FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB)); @@ -65,19 +76,16 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe public void setVisible(boolean aBool) { resetGui(); - + super.setVisible(aBool); } @Override - public void actionPerformed(ActionEvent e) + public void actionPerformed(ActionEvent aEvent) { - Object source; - ConfigItem chosenItem; + var chosenItem = listPanel.getSelectedItem(); - chosenItem = listPanel.getSelectedItem(); - - source = e.getSource(); + var source = aEvent.getSource(); if (source == deleteB) { refFrontend.delete(chosenItem.getName()); @@ -94,7 +102,7 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe { if (aEvent.getValueIsAdjusting() == true) return; - + updateGui(); } @@ -103,34 +111,27 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe */ private void buildGuiArea() { - JLabel tmpL; - QueryItemHandler aItemHandler; - QueryComposer aComposer; - - setLayout(new MigLayout("", "[grow][][]", "[][grow][]")); - - tmpL = new JLabel("Select configuration:", JLabel.CENTER); - add(tmpL, "growx,span 4,wrap"); - - // Construct the actual table panel - aComposer = new QueryComposer(); - aComposer.addAttribute(LookUp.Name, String.class, "Configuration", null); - myItemProcessor = new StaticItemProcessor(); - aItemHandler = new QueryItemHandler(aComposer); - listPanel = new ItemListPanel(aItemHandler, myItemProcessor, false, true); + var tmpL = new JLabel("Select configuration:", JLabel.CENTER); + add(tmpL, "growx,span 4,wrap"); + + // Construct the actual table panel + var tmpComposer = new QueryComposer(); + tmpComposer.addAttribute(LookUp.Name, String.class, "Configuration", null); + + myItemProcessor = new StaticItemProcessor<>(); + var tmpIH = new QueryItemHandler(); + listPanel = new ItemListPanel<>(tmpIH, myItemProcessor, tmpComposer, true); listPanel.addListSelectionListener(this); add(listPanel, "growx,growy,span 4,wrap"); - + // Control area deleteB = GuiUtil.createJButton("Delete", this); closeB = GuiUtil.createJButton("Close", this); - add(deleteB, "skip 1,span 1"); - add(closeB, "span 1"); - - setBorder(new BevelBorder(BevelBorder.RAISED)); + add(deleteB, "skip 1"); + add(closeB, ""); } /** @@ -138,31 +139,19 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe */ private void resetGui() { - Collection strList; - Collection itemList; - - itemList = Lists.newLinkedList(); - - strList = refFrontend.getSettings(); - for (String aStr : strList) + var itemL = new ArrayList(); + var strL = refFrontend.getSettings(); + for (String aStr : strL) { // Add only non reserved items if (aStr.charAt(0) != '.') - itemList.add(new ConfigItem(aStr)); + itemL.add(new ConfigItem(aStr)); } - - myItemProcessor.setItems(itemList); - - // TODO: Ugly code: Should be able to just call updateGui but can not - SwingUtilities.invokeLater(new Runnable() - { - - @Override - public void run() - { - updateGui(); - } - }); + + myItemProcessor.setItems(itemL); + + // TODO: Should be able to just call updateGui but can not + SwingUtilities.invokeLater(() -> updateGui()); } /** @@ -170,58 +159,46 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe */ private void updateGui() { - ConfigItem chosenItem; - boolean isEnabled; - - chosenItem = listPanel.getSelectedItem(); - + var chosenItem = listPanel.getSelectedItem(); if (chosenItem != null) refFrontend.load(chosenItem.getName()); - - isEnabled = chosenItem != null; + + var isEnabled = chosenItem != null; deleteB.setEnabled(isEnabled); - + // Ensure we have the focus. -//listPanel.requestFocusInWindow(); +//listPanel.requestFocusInWindow(); repaint(); - // TODO: Ugly code: Not sure why need to request focus multiple times to ensure we regrab + // TODO: Ugly code: Not sure why need to request focus multiple times to ensure we regrab // the focus. This is particularly true when there are multiple DockStations located on different // windows - SwingUtilities.invokeLater(new Runnable() - { - + SwingUtilities.invokeLater(new Runnable() { + @Override public void run() { listPanel.getTable().requestFocus(); - - SwingUtilities.invokeLater(new Runnable() - { - - @Override - public void run() - { - listPanel.getTable().requestFocus(); - } - }); - -// listPanel.getTable().getFocusCycleRootAncestor().setFocusCycleRoot(true); + + SwingUtilities.invokeLater(() -> listPanel.getTable().requestFocus()); + +// listPanel.getTable().getFocusCycleRootAncestor().setFocusCycleRoot(true); listPanel.getTable().requestFocusInWindow(); repaint(); } }); } - - + /** - * Internal class only used to wrap named (string) settings into - * a QueryItem + * Internal class only used to wrap named (string) settings into a QueryItem. + * + * @author lopeznr1 */ class ConfigItem implements QueryItem { - private String refName; - + // Attributes + private final String refName; + public ConfigItem(String aName) { refName = aName; @@ -246,7 +223,7 @@ public class FrontendManageConfigPanel extends GlassPanel implements ActionListe { throw new UnsupportedOperationException(); } - + } } diff --git a/src/glum/gui/dock/LookUp.java b/src/glum/gui/dock/LookUp.java index 838d67d..8191f66 100644 --- a/src/glum/gui/dock/LookUp.java +++ b/src/glum/gui/dock/LookUp.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; public enum LookUp diff --git a/src/glum/gui/dock/PlainDockSituationIgnore.java b/src/glum/gui/dock/PlainDockSituationIgnore.java index 3617a25..d457143 100644 --- a/src/glum/gui/dock/PlainDockSituationIgnore.java +++ b/src/glum/gui/dock/PlainDockSituationIgnore.java @@ -1,9 +1,21 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; +import java.util.LinkedHashSet; import java.util.Set; -import com.google.common.collect.Sets; - import bibliothek.gui.DockStation; import bibliothek.gui.dock.DockElement; import bibliothek.gui.dock.layout.DockSituationIgnore; @@ -12,50 +24,52 @@ import bibliothek.gui.dock.perspective.PerspectiveStation; public class PlainDockSituationIgnore implements DockSituationIgnore { - private Set> ignoreClassSet; - + // State vars + private Set> ignoreClassS; + + /** Standard Constructor */ public PlainDockSituationIgnore() { - ignoreClassSet = Sets.newLinkedHashSet(); + ignoreClassS = new LinkedHashSet<>(); } - + /** * Method to register a new type of Dockable/Station to ignore */ public void addIgnoreClass(Class aClass) { - ignoreClassSet.add(aClass); + ignoreClassS.add(aClass); } - + @Override public boolean ignoreElement(PerspectiveElement aElement) { // TODO Auto-generated method stub return false; } - + @Override public boolean ignoreElement(DockElement aElement) { // Check to see if aElement is of one of the types in ignoreClassSet - for (Class aClass : ignoreClassSet) + for (var aClass : ignoreClassS) { if (aClass.isAssignableFrom(aElement.getClass()) == true) return true; } - + return false; } - + @Override - public boolean ignoreChildren(PerspectiveStation station) + public boolean ignoreChildren(PerspectiveStation aStation) { // TODO Auto-generated method stub return false; } - + @Override - public boolean ignoreChildren(DockStation station) + public boolean ignoreChildren(DockStation aStation) { // TODO Auto-generated method stub return false; diff --git a/src/glum/gui/dock/PrimConfig.java b/src/glum/gui/dock/PrimConfig.java index 1dfb05a..d246ff5 100644 --- a/src/glum/gui/dock/PrimConfig.java +++ b/src/glum/gui/dock/PrimConfig.java @@ -1,211 +1,196 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +import java.io.*; +import java.util.LinkedHashMap; import java.util.Map; -import java.util.Set; - -import com.google.common.collect.Maps; import glum.io.IoUtil; +/** + * Container which holds a mapping of keys (String) to a bunch of primitive objects. + *

+ * Serialization of content is supported via {@link #readBin(DataInputStream)} and {@link #writeBin(DataOutputStream)} + * + * @author lopeznr1 + */ public class PrimConfig { // Constants - public static final byte CODE_BOOL = 1; - public static final byte CODE_INT = 10; - public static final byte CODE_LONG = 11; - public static final byte CODE_SHORT = 12; - public static final byte CODE_FLOAT = 13; + public static final byte CODE_BOOL = 1; + public static final byte CODE_INT = 10; + public static final byte CODE_LONG = 11; + public static final byte CODE_SHORT = 12; + public static final byte CODE_FLOAT = 13; public static final byte CODE_DOUBLE = 14; public static final byte CODE_STRING = 20; // State vars - protected Map mySingletonMap; - - /** - * Container which holds a mapping of keys (String) to a bunch - * of primitive objects. - */ + protected Map mySingletonM; + + /** Standard Constructor */ public PrimConfig() { - mySingletonMap = Maps.newLinkedHashMap(); + mySingletonM = new LinkedHashMap<>(); } /** - * Returns the boolean associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the boolean associated with aKey If no such value is found then defaultVal is returned. */ - public boolean getBoolean(String aKey, boolean defaultVal) + public boolean getBoolean(String aKey, boolean aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Boolean.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Boolean.class) + return aDefaultVal; - return (Boolean)aValue; + return (Boolean) tmpVal; } - + /** - * Returns the int associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the int associated with aKey If no such value is found then defaultVal is returned. */ - public int getInt(String aKey, int defaultVal) + public int getInt(String aKey, int aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Integer.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Integer.class) + return aDefaultVal; - return (Integer)aValue; + return (Integer) tmpVal; } - + /** - * Returns the long associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the long associated with aKey If no such value is found then defaultVal is returned. */ - public long getLong(String aKey, long defaultVal) + public long getLong(String aKey, long aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Long.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Long.class) + return aDefaultVal; - return (Long)aValue; + return (Long) tmpVal; } - + /** - * Returns the short associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the short associated with aKey If no such value is found then defaultVal is returned. */ - public short getShort(String aKey, short defaultVal) + public short getShort(String aKey, short aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Short.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Short.class) + return aDefaultVal; - return (Short)aValue; + return (Short) tmpVal; } /** - * Returns the float associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the float associated with aKey If no such value is found then defaultVal is returned. */ - public float getFloat(String aKey, float defaultVal) + public float getFloat(String aKey, float aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Float.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Float.class) + return aDefaultVal; - return (Float)aValue; + return (Float) tmpVal; } /** - * Returns the double associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the double associated with aKey If no such value is found then defaultVal is returned. */ - public double getDouble(String aKey, double defaultVal) + public double getDouble(String aKey, double aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != Double.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != Double.class) + return aDefaultVal; - return (Double)aValue; + return (Double) tmpVal; } /** - * Returns the String associated with aKey - * If no such value is found then defaultVal is returned. + * Returns the String associated with aKey If no such value is found then defaultVal is returned. */ - public String getString(String aKey, String defaultVal) + public String getString(String aKey, String aDefaultVal) { - Object aValue; - // If no value associated with the key then return defaultVal - aValue = mySingletonMap.get(aKey); - if (aValue == null || aValue.getClass() != String.class) - return defaultVal; + var tmpVal = mySingletonM.get(aKey); + if (tmpVal == null || tmpVal.getClass() != String.class) + return aDefaultVal; - return (String)aValue; + return (String) tmpVal; } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setBoolean(String aKey, boolean aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setInt(String aKey, int aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setLong(String aKey, long aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setShort(String aKey, short aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setFloat(String aKey, float aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setDouble(String aKey, double aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** - * Associates aVal with aKey. Note this will overwrite any - * previous association. + * Associates aVal with aKey. Note this will overwrite any previous association. */ public void setString(String aKey, String aValue) { - mySingletonMap.put(aKey, aValue); + mySingletonM.put(aKey, aValue); } /** @@ -213,50 +198,46 @@ public class PrimConfig */ public void readBin(DataInputStream aStream) throws IOException { - int numItems; - String aKey; - byte aType; + mySingletonM.clear(); - mySingletonMap.clear(); - - numItems = aStream.readInt(); + var numItems = aStream.readInt(); for (int c1 = 0; c1 < numItems; c1++) { - aKey = IoUtil.readString(aStream); - aType = aStream.readByte(); - - switch(aType) + var tmpKey = IoUtil.readString(aStream); + var tmpType = aStream.readByte(); + + switch (tmpType) { case CODE_BOOL: - mySingletonMap.put(aKey, aStream.readBoolean()); + mySingletonM.put(tmpKey, aStream.readBoolean()); break; - + case CODE_INT: - mySingletonMap.put(aKey, aStream.readInt()); + mySingletonM.put(tmpKey, aStream.readInt()); break; - + case CODE_LONG: - mySingletonMap.put(aKey, aStream.readLong()); + mySingletonM.put(tmpKey, aStream.readLong()); break; - + case CODE_SHORT: - mySingletonMap.put(aKey, aStream.readShort()); + mySingletonM.put(tmpKey, aStream.readShort()); break; - + case CODE_FLOAT: - mySingletonMap.put(aKey, aStream.readFloat()); + mySingletonM.put(tmpKey, aStream.readFloat()); break; - + case CODE_DOUBLE: - mySingletonMap.put(aKey, aStream.readDouble()); + mySingletonM.put(tmpKey, aStream.readDouble()); break; - + case CODE_STRING: - mySingletonMap.put(aKey, IoUtil.readString(aStream)); + mySingletonM.put(tmpKey, IoUtil.readString(aStream)); break; - + default: - throw new RuntimeException("Unreconnized type: " + aType); + throw new RuntimeException("Unreconnized type: " + tmpType); } } } @@ -266,55 +247,52 @@ public class PrimConfig */ public void writeBin(DataOutputStream aStream) throws IOException { - Set keySet; - Object aVal; - - keySet = mySingletonMap.keySet(); - - aStream.writeInt(keySet.size()); - for (String aKey : keySet) + var keyS = mySingletonM.keySet(); + + aStream.writeInt(keyS.size()); + for (String aKey : keyS) { IoUtil.writeString(aStream, aKey); - - aVal = mySingletonMap.get(aKey); - if (aVal instanceof Boolean) + + var tmpVal = mySingletonM.get(aKey); + if (tmpVal instanceof Boolean) { aStream.writeByte(CODE_BOOL); - aStream.writeBoolean((Boolean)aVal); + aStream.writeBoolean((Boolean) tmpVal); } - else if (aVal instanceof Integer) + else if (tmpVal instanceof Integer) { aStream.writeByte(CODE_INT); - aStream.writeInt((Integer)aVal); + aStream.writeInt((Integer) tmpVal); } - else if (aVal instanceof Long) + else if (tmpVal instanceof Long) { aStream.writeByte(CODE_LONG); - aStream.writeLong((Long)aVal); + aStream.writeLong((Long) tmpVal); } - else if (aVal instanceof Short) + else if (tmpVal instanceof Short) { aStream.writeByte(CODE_SHORT); - aStream.writeLong((Short)aVal); + aStream.writeLong((Short) tmpVal); } - else if (aVal instanceof Float) + else if (tmpVal instanceof Float) { aStream.writeByte(CODE_FLOAT); - aStream.writeFloat((Float)aVal); + aStream.writeFloat((Float) tmpVal); } - else if (aVal instanceof Double) + else if (tmpVal instanceof Double) { aStream.writeByte(CODE_DOUBLE); - aStream.writeDouble((Double)aVal); + aStream.writeDouble((Double) tmpVal); } - else if (aVal instanceof String) + else if (tmpVal instanceof String) { aStream.writeByte(CODE_STRING); - IoUtil.writeString(aStream, (String)aVal); + IoUtil.writeString(aStream, (String) tmpVal); } else { - throw new RuntimeException("Unsupported Object: " + aVal); + throw new RuntimeException("Unsupported Object: " + tmpVal); } } } diff --git a/src/glum/gui/dock/PrimDock.java b/src/glum/gui/dock/PrimDock.java index ada6192..cd29b15 100644 --- a/src/glum/gui/dock/PrimDock.java +++ b/src/glum/gui/dock/PrimDock.java @@ -1,28 +1,41 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import bibliothek.gui.dock.DockElement; /** - * Base class for Dockables which would like to store their configuration via - * the PrimConfig mechanism. Note if the child class will be loaded with the - * PrimDockableFactory, then you should have a constructor with one of the - * following properties:. - *

  • 1 arguments: Registry - *
  • 0 arguments: Empty Constructor - *
    - *
    It is also important the method getFactoryID() is overridden so that it returns PrimDockableFactory.ID + * Base class for Dockables which would like to store their configuration via the PrimConfig mechanism. Note if the + * child class will be loaded with the PrimDockableFactory, then you should have a constructor with one of the following + * properties:. + *
      + *
    • 1 arguments: Registry + *
    • 0 arguments: Empty Constructor + *
    + * It is also important the method getFactoryID() is overridden so that it returns PrimDockableFactory.ID + * + * @author lopeznr1 */ public interface PrimDock extends DockElement { /** - * Returns the PrimConfig associated with the Dockable - * @return + * Returns the PrimConfig associated with the Dockable. */ public abstract PrimConfig getConfiguration(); /** - * Configures the Dockable with the aConfig - * @return + * Configures the Dockable with the aConfig. */ public abstract void setConfiguration(PrimConfig aConfig); diff --git a/src/glum/gui/dock/PrimDockFactory.java b/src/glum/gui/dock/PrimDockFactory.java index 2be01d8..6748571 100644 --- a/src/glum/gui/dock/PrimDockFactory.java +++ b/src/glum/gui/dock/PrimDockFactory.java @@ -1,16 +1,25 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; -import glum.reflect.ReflectUtil; -import glum.registry.Registry; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +import java.io.*; import java.lang.reflect.Constructor; import java.util.Map; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; + import bibliothek.gui.Dockable; import bibliothek.gui.dock.DockFactory; import bibliothek.gui.dock.dockable.DefaultDockablePerspective; @@ -18,47 +27,52 @@ import bibliothek.gui.dock.layout.LocationEstimationMap; import bibliothek.gui.dock.perspective.PerspectiveDockable; import bibliothek.gui.dock.station.support.PlaceholderStrategy; import bibliothek.util.xml.XElement; +import glum.reflect.ReflectUtil; +import glum.registry.Registry; /** * Generic DockableFactory for creating PrimDocks. - *

    + *

    * Note that before this factory is used all PrimDockable class types must first be associated with a spawnName. This is * used during serialization configuration associated with PrimDock. See method {@link PrimDockFactory#addSpawnMapping} + * + * @author lopeznr1 */ public class PrimDockFactory implements DockFactory { // Constants public static final String ID = "PrimDockFactory"; - public static final String SpawnNameKey = "factory.spawnName"; - + public static final String SpawnNameKey = "factory.spawnName"; + // State var - protected Registry refRegistry; - protected BiMap> spawnMap; - + private Registry refRegistry; + private BiMap> spawnM; + + /** Standard Constructor */ public PrimDockFactory(Registry aRegistry) { refRegistry = aRegistry; - spawnMap = HashBiMap.create(); + spawnM = HashBiMap.create(); } - + /** * Add a mapping for a PrimDockable to a the associated spawnName. It is mandatory * that this mapping is always the same regardless of application executions, as - * this value will be serialized to the disk. + * this value will be serialized to the disk. */ - public void addSpawnMapping(String spawnName, Class spawnClass) + public void addSpawnMapping(String aSpawnName, Class aSpawnClass) { // Ensure the spawnName is not already reserved - if (spawnMap.containsKey(spawnName) == true) - throw new RuntimeException("Previous mapping stored for spawnName:" + spawnName); - + if (spawnM.containsKey(aSpawnName) == true) + throw new RuntimeException("Previous mapping stored for spawnName:" + aSpawnName); + // Ensure the spawnClass is not already stored - if (spawnMap.inverse().containsKey(spawnClass) == true) - throw new RuntimeException("Previous mapping stored for spawnClass:" + spawnClass); - - spawnMap.put(spawnName, spawnClass); + if (spawnM.inverse().containsKey(aSpawnClass) == true) + throw new RuntimeException("Previous mapping stored for spawnClass:" + aSpawnClass); + + spawnM.put(aSpawnName, aSpawnClass); } - + @Override public String getID() @@ -71,18 +85,18 @@ public class PrimDockFactory implements DockFactory children, PlaceholderStrategy placeholders) { PrimDock aDockable; - + aDockable = layout(layout, placeholders); return aDockable; } @@ -157,13 +171,13 @@ public class PrimDockFactory implements DockFactory parmTypes[] = {Registry.class}; Object parmValues[] = {refRegistry}; String spawnName; - + spawnName = aLayout.getString(SpawnNameKey, null); - - spawnClass = spawnMap.get(spawnName); + + spawnClass = spawnM.get(spawnName); if (spawnClass == null) throw new RuntimeException("Factory is not configured properly. Failed to locate associated class for spawn name:" + spawnName); - + try { spawnConstructor = ReflectUtil.getConstructorSafe(spawnClass, parmTypes); @@ -176,7 +190,7 @@ public class PrimDockFactory implements DockFactory children) + public void layoutPerspective(DefaultDockablePerspective perspective, PrimConfig layout, + Map children) { ; // Nothing to do } diff --git a/src/glum/gui/dock/PrimDockable.java b/src/glum/gui/dock/PrimDockable.java index 5e37117..fc34137 100644 --- a/src/glum/gui/dock/PrimDockable.java +++ b/src/glum/gui/dock/PrimDockable.java @@ -1,14 +1,30 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock; import bibliothek.gui.dock.DefaultDockable; /** - * Base class for Dockables which would like to store their configuration via - * the PrimConfig mechanism. Note if the child class will be loaded with the - * PrimDockableFactory, then you should have a constructor with one of the - * following properties:. - *

  • 1 arguments: Registry - *
  • 0 arguments: Empty Constructor + * Base class for Dockables which would like to store their configuration via the PrimConfig mechanism. Note if the + * child class will be loaded with the PrimDockableFactory, then you should have a constructor with one of the following + * properties:. + *
      + *
    • 1 arguments: Registry + *
    • 0 arguments: Empty Constructor + *
    + * + * @author lopeznr1 */ public abstract class PrimDockable extends DefaultDockable implements PrimDock { diff --git a/src/glum/gui/dock/action/Closeable.java b/src/glum/gui/dock/action/Closeable.java index c1995dc..1f9d755 100644 --- a/src/glum/gui/dock/action/Closeable.java +++ b/src/glum/gui/dock/action/Closeable.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; public interface Closeable diff --git a/src/glum/gui/dock/action/Destroyable.java b/src/glum/gui/dock/action/Destroyable.java index 7acc10b..69312cc 100644 --- a/src/glum/gui/dock/action/Destroyable.java +++ b/src/glum/gui/dock/action/Destroyable.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; public interface Destroyable diff --git a/src/glum/gui/dock/action/DismissAction.java b/src/glum/gui/dock/action/DismissAction.java index 991a87e..00071bd 100644 --- a/src/glum/gui/dock/action/DismissAction.java +++ b/src/glum/gui/dock/action/DismissAction.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; import javax.swing.Icon; diff --git a/src/glum/gui/dock/action/MakeVisibleAction.java b/src/glum/gui/dock/action/MakeVisibleAction.java index f40dc08..443fffc 100644 --- a/src/glum/gui/dock/action/MakeVisibleAction.java +++ b/src/glum/gui/dock/action/MakeVisibleAction.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; import javax.swing.Icon; diff --git a/src/glum/gui/dock/action/SimpleDockAction.java b/src/glum/gui/dock/action/SimpleDockAction.java index d75e134..e002d8b 100644 --- a/src/glum/gui/dock/action/SimpleDockAction.java +++ b/src/glum/gui/dock/action/SimpleDockAction.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; import java.awt.event.ActionEvent; diff --git a/src/glum/gui/dock/action/ToggleAction.java b/src/glum/gui/dock/action/ToggleAction.java index eed2ec1..bf89008 100644 --- a/src/glum/gui/dock/action/ToggleAction.java +++ b/src/glum/gui/dock/action/ToggleAction.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.action; import javax.swing.Icon; @@ -7,21 +20,22 @@ import bibliothek.gui.dock.action.actions.SimpleButtonAction; /** * An DockAction that will fire trigger an embedded java.awt Action + * + * @author lopeznr1 */ public class ToggleAction extends SimpleButtonAction { // State vars - protected boolean isActive; - + private boolean isActive; + // Gui vars - protected Icon falseIcon, trueIcon; - + private final Icon falseIcon, trueIcon; + + /** Standard Constructor */ public ToggleAction(String aText, Icon aFalseIcon, Icon aTrueIcon, boolean aIsActive) { - super(); - isActive = aIsActive; - + falseIcon = aFalseIcon; trueIcon = aTrueIcon; @@ -36,24 +50,24 @@ public class ToggleAction extends SimpleButtonAction { return isActive; } - + public void setIsActive(boolean aBool) { isActive = aBool; updateGui(); } - + @Override public void action(Dockable aDockable) { isActive = !isActive; updateGui(); - + super.action(aDockable); } /** - * Utility method + * Utility method */ private void updateGui() { diff --git a/src/glum/gui/dock/alt/AltScreenDockFrame.java b/src/glum/gui/dock/alt/AltScreenDockFrame.java index e161d2f..3aec91b 100644 --- a/src/glum/gui/dock/alt/AltScreenDockFrame.java +++ b/src/glum/gui/dock/alt/AltScreenDockFrame.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.alt; import glum.gui.dock.action.Destroyable; diff --git a/src/glum/gui/dock/alt/AltScreenDockStation.java b/src/glum/gui/dock/alt/AltScreenDockStation.java index a46b25a..8233213 100644 --- a/src/glum/gui/dock/alt/AltScreenDockStation.java +++ b/src/glum/gui/dock/alt/AltScreenDockStation.java @@ -1,42 +1,58 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.alt; -import glum.gui.dock.BaseDockable; - import java.awt.Window; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Set; -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - import bibliothek.gui.Dockable; import bibliothek.gui.dock.ScreenDockStation; import bibliothek.gui.dock.action.DefaultDockActionSource; import bibliothek.gui.dock.action.DockAction; import bibliothek.gui.dock.action.DockActionSource; import bibliothek.gui.dock.action.LocationHint; +import glum.gui.dock.BaseDockable; /** * Alternative ScreenDockStation which provides no default direct/indirect action offers. + * + * @author lopeznr1 */ public class AltScreenDockStation extends ScreenDockStation { // Action vars - private List directDockActionList; - private List indirectDockActionList; + private List directDockActionL; + private List indirectDockActionL; // Lock vars - private Set lockSet; + private Set lockS; private boolean isLocked; - public AltScreenDockStation(Window owner) + /** + * Standard Constructor + */ + public AltScreenDockStation(Window aOwner) { - super(owner); + super(aOwner); - directDockActionList = Lists.newArrayList(); - indirectDockActionList = Lists.newArrayList(); + directDockActionL = new ArrayList<>(); + indirectDockActionL = new ArrayList<>(); - lockSet = Sets.newHashSet(); + lockS = new HashSet<>(); isLocked = false; } @@ -45,7 +61,7 @@ public class AltScreenDockStation extends ScreenDockStation */ public void addDirectActionOffer(DockAction aDockAction) { - directDockActionList.add(aDockAction); + directDockActionL.add(aDockAction); } /** @@ -53,7 +69,7 @@ public class AltScreenDockStation extends ScreenDockStation */ public void addIndirectActionOffer(DockAction aDockAction) { - indirectDockActionList.add(aDockAction); + indirectDockActionL.add(aDockAction); } /** @@ -66,14 +82,14 @@ public class AltScreenDockStation extends ScreenDockStation if (isLocked == false) { - lockSet.clear(); + lockS.clear(); return; } // Record all of the valid children when the lock is triggered for (int c1 = 0; c1 < getDockableCount(); c1++) { - lockSet.add(getDockable(c1)); + lockS.add(getDockable(c1)); } } @@ -82,34 +98,34 @@ public class AltScreenDockStation extends ScreenDockStation public boolean accept(Dockable aChild) { // If we are locked then never accept any Dockable, which was not recorded as valid when the lock happened - if (isLocked == true && lockSet.contains(aChild) == false) + if (isLocked == true && lockS.contains(aChild) == false) return false; // Never accept any Dockable that has been marked as nontransferable if (aChild instanceof BaseDockable) - return ((BaseDockable)aChild).isTransferable(this); + return ((BaseDockable) aChild).isTransferable(this); return super.accept(aChild); } @Override - public DefaultDockActionSource getDirectActionOffers(Dockable dockable) + public DefaultDockActionSource getDirectActionOffers(Dockable aDockable) { DefaultDockActionSource source; source = new DefaultDockActionSource(new LocationHint(LocationHint.DIRECT_ACTION, LocationHint.VERY_RIGHT)); - source.add(directDockActionList.toArray(new DockAction[0])); + source.add(directDockActionL.toArray(new DockAction[0])); return source; } @Override - public DockActionSource getIndirectActionOffers(Dockable dockable) + public DockActionSource getIndirectActionOffers(Dockable aDockable) { DefaultDockActionSource source; source = new DefaultDockActionSource(new LocationHint(LocationHint.INDIRECT_ACTION, LocationHint.VERY_RIGHT)); - source.add(indirectDockActionList.toArray(new DockAction[0])); + source.add(indirectDockActionL.toArray(new DockAction[0])); return source; } diff --git a/src/glum/gui/dock/alt/AltScreenDockWindowFactory.java b/src/glum/gui/dock/alt/AltScreenDockWindowFactory.java index 6c0aba7..b62b4b9 100644 --- a/src/glum/gui/dock/alt/AltScreenDockWindowFactory.java +++ b/src/glum/gui/dock/alt/AltScreenDockWindowFactory.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.alt; import java.awt.Dialog; diff --git a/src/glum/gui/dock/alt/AltSplitDockStation.java b/src/glum/gui/dock/alt/AltSplitDockStation.java index c1e3e0b..dc09b51 100644 --- a/src/glum/gui/dock/alt/AltSplitDockStation.java +++ b/src/glum/gui/dock/alt/AltSplitDockStation.java @@ -1,39 +1,49 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.dock.alt; -import glum.gui.dock.BaseDockable; -import java.util.List; -import java.util.Set; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; +import java.util.*; import bibliothek.gui.Dockable; import bibliothek.gui.dock.SplitDockStation; -import bibliothek.gui.dock.action.DefaultDockActionSource; -import bibliothek.gui.dock.action.DockAction; -import bibliothek.gui.dock.action.DockActionSource; -import bibliothek.gui.dock.action.LocationHint; +import bibliothek.gui.dock.action.*; +import glum.gui.dock.BaseDockable; +/** + * Alternative implementation of SplitDockStation. + * + * @author lopeznr1 + */ public class AltSplitDockStation extends SplitDockStation { // Action vars - private List directDockActionList; - private List localDockActionList; - private List indirectDockActionList; + private List directDockActionL; + private List localDockActionL; + private List indirectDockActionL; // Lock vars - private Set lockSet; + private Set lockS; private boolean isLocked; + /** Standard Constructor */ public AltSplitDockStation() { - super(); + directDockActionL = new ArrayList<>(); + localDockActionL = new ArrayList<>(); + indirectDockActionL = new ArrayList<>(); - directDockActionList = Lists.newArrayList(); - localDockActionList = Lists.newArrayList(); - indirectDockActionList = Lists.newArrayList(); - - lockSet = Sets.newHashSet(); + lockS = new HashSet<>(); isLocked = false; } @@ -42,7 +52,7 @@ public class AltSplitDockStation extends SplitDockStation */ public void addDirectActionOffer(DockAction aDockAction) { - directDockActionList.add(aDockAction); + directDockActionL.add(aDockAction); } /** @@ -50,7 +60,7 @@ public class AltSplitDockStation extends SplitDockStation */ public void addLocalActionOffer(DockAction aDockAction) { - localDockActionList.add(aDockAction); + localDockActionL.add(aDockAction); } /** @@ -58,7 +68,7 @@ public class AltSplitDockStation extends SplitDockStation */ public void addIndirectActionOffer(DockAction aDockAction) { - indirectDockActionList.add(aDockAction); + indirectDockActionL.add(aDockAction); } /** @@ -71,14 +81,14 @@ public class AltSplitDockStation extends SplitDockStation if (isLocked == false) { - lockSet.clear(); + lockS.clear(); return; } // Record all of the valid children when the lock is triggered for (int c1 = 0; c1 < getDockableCount(); c1++) { - lockSet.add(getDockable(c1)); + lockS.add(getDockable(c1)); } } @@ -87,18 +97,18 @@ public class AltSplitDockStation extends SplitDockStation public boolean accept(Dockable aChild) { // If we are locked then never accept any Dockable, which was not recorded as valid when the lock happened - if (isLocked == true && lockSet.contains(aChild) == false) + if (isLocked == true && lockS.contains(aChild) == false) return false; // Never accept any Dockable that has been marked as nontransferable if (aChild instanceof BaseDockable) - return ((BaseDockable)aChild).isTransferable(this); + return ((BaseDockable) aChild).isTransferable(this); // Default behavior for non BaseDockables return super.accept(aChild); } -// +// // @Override // protected boolean acceptable(Dockable old, Dockable next) // { @@ -112,8 +122,8 @@ public class AltSplitDockStation extends SplitDockStation //// if (((BaseDockable)next).isTransferable() == false) //// return false; //// } -//// -//// +//// +//// //// // TODO Auto-generated method stub //// return super.acceptable(old, next); // } @@ -127,7 +137,7 @@ public class AltSplitDockStation extends SplitDockStation // return super.canDrag(aDockable); // } // -// +// // @Override // public boolean canReplace(Dockable oldDockable, Dockable nextDockable) // { @@ -141,12 +151,10 @@ public class AltSplitDockStation extends SplitDockStation // } @Override - public DefaultDockActionSource getDirectActionOffers(Dockable dockable) + public DefaultDockActionSource getDirectActionOffers(Dockable aDockable) { - DefaultDockActionSource source; - - source = new DefaultDockActionSource(new LocationHint(LocationHint.DIRECT_ACTION, LocationHint.VERY_RIGHT)); - source.add(directDockActionList.toArray(new DockAction[0])); + var source = new DefaultDockActionSource(new LocationHint(LocationHint.DIRECT_ACTION, LocationHint.VERY_RIGHT)); + source.add(directDockActionL.toArray(new DockAction[0])); return source; } @@ -154,21 +162,17 @@ public class AltSplitDockStation extends SplitDockStation @Override public DockActionSource getLocalActionOffers() { - DefaultDockActionSource source; - - source = new DefaultDockActionSource(new LocationHint(LocationHint.DOCKABLE, LocationHint.RIGHT)); - source.add(localDockActionList.toArray(new DockAction[0])); + var source = new DefaultDockActionSource(new LocationHint(LocationHint.DOCKABLE, LocationHint.RIGHT)); + source.add(localDockActionL.toArray(new DockAction[0])); return source; } @Override - public DockActionSource getIndirectActionOffers(Dockable dockable) + public DockActionSource getIndirectActionOffers(Dockable aDockable) { - DefaultDockActionSource source; - - source = new DefaultDockActionSource(new LocationHint(LocationHint.INDIRECT_ACTION, LocationHint.VERY_RIGHT)); - source.add(indirectDockActionList.toArray(new DockAction[0])); + var source = new DefaultDockActionSource(new LocationHint(LocationHint.INDIRECT_ACTION, LocationHint.VERY_RIGHT)); + source.add(indirectDockActionL.toArray(new DockAction[0])); return source; } diff --git a/src/glum/gui/document/BaseDocument.java b/src/glum/gui/document/BaseDocument.java index 64926aa..160a230 100644 --- a/src/glum/gui/document/BaseDocument.java +++ b/src/glum/gui/document/BaseDocument.java @@ -1,24 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.document; import java.awt.event.*; -import javax.swing.text.*; + import javax.swing.JTextField; +import javax.swing.text.PlainDocument; public abstract class BaseDocument extends PlainDocument implements ActionListener, FocusListener { // State vars protected JTextField ownerTF; + /** Standard Constructor */ public BaseDocument(JTextField aOwnerTF) { - super(); - ownerTF = aOwnerTF; } /** * Get the owner of this Document model - * Todo: This method should no longer be needed. + *

    + * TODO: This method should no longer be needed. */ public JTextField getOwner() { diff --git a/src/glum/gui/document/BaseNumberDocument.java b/src/glum/gui/document/BaseNumberDocument.java index a1973fe..7bec794 100644 --- a/src/glum/gui/document/BaseNumberDocument.java +++ b/src/glum/gui/document/BaseNumberDocument.java @@ -1,23 +1,40 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.document; import javax.swing.JTextField; +/** + * Implementation of {@link BaseDocument} specific for numerical input. + * + * @author lopeznr1 + */ public abstract class BaseNumberDocument extends BaseDocument { // State vars protected double minVal, maxVal; protected boolean formalizeDoc; - protected int numAvailCols; + /** Standard Constructor */ public BaseNumberDocument(JTextField aOwner, double aMinVal, double aMaxVal) { super(aOwner); - + setMinMaxValue(aMinVal, aMaxVal); formalizeDoc = false; - numAvailCols = -1; } - + /** * Updates the new range of valid numbers. */ @@ -25,12 +42,12 @@ public abstract class BaseNumberDocument extends BaseDocument { minVal = aMinVal; maxVal = aMaxVal; - + // Insanity check if (minVal >= maxVal) throw new RuntimeException("Illogical range. Range: [" + minVal + "," + maxVal + "]"); } - + @Override public void formalizeInput() { @@ -56,6 +73,4 @@ public abstract class BaseNumberDocument extends BaseDocument ownerTF.addFocusListener(this); } - - } diff --git a/src/glum/gui/document/CharDocument.java b/src/glum/gui/document/CharDocument.java index 9a1b9d2..51a3f12 100644 --- a/src/glum/gui/document/CharDocument.java +++ b/src/glum/gui/document/CharDocument.java @@ -1,37 +1,49 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.document; +import java.util.HashSet; import java.util.Set; import javax.swing.JTextField; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; -import com.google.common.collect.Sets; - /** * Specialized Document designed to accept only the specified input chars + * + * @author lopeznr1 */ public class CharDocument extends BaseDocument { - private Set validSet; + private Set validS; - public CharDocument(JTextField aOwner, String validCharStr) - { - this(aOwner, validCharStr, true); - } - - public CharDocument(JTextField aOwner, String validCharStr, boolean isCaseSensitive) + /** + * Standard Constructor + */ + public CharDocument(JTextField aOwner, String aValidCharStr, boolean aIsCaseSensitive) { super(aOwner); - - validSet = Sets.newHashSet(); - for (int c1 = 0; c1 < validCharStr.length(); c1++) + + validS = new HashSet<>(); + for (int c1 = 0; c1 < aValidCharStr.length(); c1++) { - validSet.add(validCharStr.charAt(c1)); - if (isCaseSensitive == false) + validS.add(aValidCharStr.charAt(c1)); + if (aIsCaseSensitive == false) { - validSet.add(Character.toLowerCase(validCharStr.charAt(c1))); - validSet.add(Character.toUpperCase(validCharStr.charAt(c1))); + validS.add(Character.toLowerCase(aValidCharStr.charAt(c1))); + validS.add(Character.toUpperCase(aValidCharStr.charAt(c1))); } } } @@ -41,25 +53,23 @@ public class CharDocument extends BaseDocument { ; // Nothing to do } - - @Override - public void insertString(int offs, String str, AttributeSet a) throws BadLocationException - { - char aChar; + @Override + public void insertString(int aOffs, String aStr, AttributeSet aAS) throws BadLocationException + { // Insanity check - if (str == null) + if (aStr == null) return; - + // Ensure all of the characters in str are in the valid set - for (int c1 = 0; c1 < str.length(); c1++) + for (int c1 = 0; c1 < aStr.length(); c1++) { - aChar = str.charAt(c1); - if (validSet.contains(aChar) == false) - throw new BadLocationException("Invalid character: " + aChar, offs); + char tmpChar = aStr.charAt(c1); + if (validS.contains(tmpChar) == false) + throw new BadLocationException("Invalid character: " + tmpChar, aOffs); } - - super.insertString(offs, str, a); + + super.insertString(aOffs, aStr, aAS); } } diff --git a/src/glum/gui/document/NumberDocument.java b/src/glum/gui/document/NumberDocument.java index ab00b93..9de1956 100644 --- a/src/glum/gui/document/NumberDocument.java +++ b/src/glum/gui/document/NumberDocument.java @@ -1,15 +1,40 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.document; -import javax.swing.text.*; import javax.swing.JTextField; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; -import glum.gui.GuiUtil; +import glum.io.ParseUtil; +/** + * Implementation of {@link BaseNumberDocument}. + * + * @author lopeznr1 + */ public class NumberDocument extends BaseNumberDocument { - protected boolean allowFloats; -// protected NumberUnit myUnit; + // Constants + private final String ValidPosDigitStr = "0123456789"; + private final String ValidNegDigitStr = "+-0123456789"; + private final String ValidFractStr = "+-0123456789e."; + protected boolean allowFloats; +// protected NumberUnit myUnit; + + /** Standard Constructor */ public NumberDocument(JTextField aOwnerTF, boolean aFormalizeDoc) { super(aOwnerTF, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); @@ -38,6 +63,28 @@ public class NumberDocument extends BaseNumberDocument if (str == null) return; + // Change to allow user to enter any valid single character to be input. Note that the + // NumberDocument is used primary by GNumberField and as such invalid input will be + // colored with the "fail" color. + if (str.length() == 1) + { + if (allowFloats == false && minVal >= 0 && ValidPosDigitStr.contains(str) == true) + { + super.insertString(offs, str, a); + return; + } + else if (allowFloats == false && minVal < 0 && ValidNegDigitStr.contains(str) == true) + { + super.insertString(offs, str, a); + return; + } + else if (allowFloats == true && ValidFractStr.contains(str) == true) + { + super.insertString(offs, str, a); + return; + } + } + // Special cases aChar = str.charAt(0); if (offs == 0) @@ -76,13 +123,6 @@ public class NumberDocument extends BaseNumberDocument if (str.contains(".") == true && allowFloats == false) throw new BadLocationException("Only integers are allowed.", offs); - // Ensure we do not exceed number of columns - if (numAvailCols > 0) - { - if (offs + str.length() >= numAvailCols) - throw new BadLocationException("Too many characters to insert.", offs); - } - // Form the resultant string bStr = ""; eStr = ""; @@ -92,7 +132,7 @@ public class NumberDocument extends BaseNumberDocument resultStr = bStr + str + eStr; // Ensure the resultant is sensical - aVal = GuiUtil.readDouble(resultStr, Double.NaN); + aVal = ParseUtil.readDouble(resultStr, Double.NaN); if (Double.isNaN(aVal) == true) throw new BadLocationException("Nonsensical number.", offs); diff --git a/src/glum/gui/icon/ArrowNorthIcon.java b/src/glum/gui/icon/ArrowNorthIcon.java index 7cb2fbe..abc8c2e 100644 --- a/src/glum/gui/icon/ArrowNorthIcon.java +++ b/src/glum/gui/icon/ArrowNorthIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import java.awt.Color; diff --git a/src/glum/gui/icon/ArrowSouthIcon.java b/src/glum/gui/icon/ArrowSouthIcon.java index b275f74..280e1ed 100644 --- a/src/glum/gui/icon/ArrowSouthIcon.java +++ b/src/glum/gui/icon/ArrowSouthIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import java.awt.Color; diff --git a/src/glum/gui/icon/BaseIcon.java b/src/glum/gui/icon/BaseIcon.java index e6b69db..6fa0849 100644 --- a/src/glum/gui/icon/BaseIcon.java +++ b/src/glum/gui/icon/BaseIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import javax.swing.Icon; diff --git a/src/glum/gui/icon/DeleteIcon.java b/src/glum/gui/icon/DeleteIcon.java index 99a39d5..d503b95 100644 --- a/src/glum/gui/icon/DeleteIcon.java +++ b/src/glum/gui/icon/DeleteIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import java.awt.BasicStroke; diff --git a/src/glum/gui/icon/EmptyIcon.java b/src/glum/gui/icon/EmptyIcon.java index 3d29e27..63c153a 100644 --- a/src/glum/gui/icon/EmptyIcon.java +++ b/src/glum/gui/icon/EmptyIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import java.awt.Component; @@ -5,11 +18,28 @@ import java.awt.Graphics; import javax.swing.Icon; +/** + * Immutable icon that renders nothing. + *

    + * This icon will typically be used as a place holder. + * + * @author lopeznr1 + */ public class EmptyIcon implements Icon { - protected int width, height; - - EmptyIcon(int aWidth, int aHeight) + // Attributes + private final int width; + private final int height; + + /** + * Standard Constructor + * + * @param aWidth + * The width of the icon. + * @param aHeight + * The height of the icon. + */ + public EmptyIcon(int aWidth, int aHeight) { width = aWidth; height = aHeight; diff --git a/src/glum/gui/icon/IconUtil.java b/src/glum/gui/icon/IconUtil.java index 0a5d305..9d1df82 100644 --- a/src/glum/gui/icon/IconUtil.java +++ b/src/glum/gui/icon/IconUtil.java @@ -1,24 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.icon; import java.awt.Component; +import java.net.URL; + import javax.swing.ImageIcon; import javax.swing.JButton; +/** + * Collection of utility methods for dealing with icons. + * + * @author lopeznr1 + */ public class IconUtil { /** - * Utility method to load the Icon specified by the resource path. + * Utility method to load an icon from the specified resource. */ - public static ImageIcon loadIcon(String iconPath) + public static ImageIcon loadIcon(String aIconPath) { -// URL aURL; -// -// aURL = IconUtil.class.getClassLoader().getResource(iconPath); -// if (aURL == null) -// throw new RuntimeException("Failed to load icon for path: " + iconPath); -// -// return new ImageIcon(aURL); - return new ImageIcon(ClassLoader.getSystemResource(iconPath)); + return new ImageIcon(ClassLoader.getSystemResource(aIconPath)); + } + + /** + * Utility method to load an icon from the specified resource. + */ + public static ImageIcon loadIcon(URL aURL) + { + return new ImageIcon(aURL); } /** @@ -28,8 +49,8 @@ public class IconUtil { if (aComp instanceof JButton == false) return false; - - return ((JButton)aComp).getModel().isPressed(); + + return ((JButton) aComp).getModel().isPressed(); } } diff --git a/src/glum/gui/info/FilePathInfo.java b/src/glum/gui/info/FilePathInfo.java index 3ef7b7a..c1121c0 100644 --- a/src/glum/gui/info/FilePathInfo.java +++ b/src/glum/gui/info/FilePathInfo.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.info; import glum.zio.*; diff --git a/src/glum/gui/info/WindowCfg.java b/src/glum/gui/info/WindowCfg.java new file mode 100644 index 0000000..5c4bd25 --- /dev/null +++ b/src/glum/gui/info/WindowCfg.java @@ -0,0 +1,162 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.info; + +import java.awt.Component; +import java.awt.Dimension; +import java.io.IOException; + +import glum.zio.*; + +/** + * Immutable object used to store the position/dimension/visibility state of a {@link Component}. + *

    + * This is typically used capture the state of a dialog or window. + * + * @author lopeznr1 + */ +public class WindowCfg implements ZioRaw +{ + // Attributes + private final boolean isShown; + private final int posX; + private final int posY; + private final int dimX; + private final int dimY; + + /** Standard Constructor */ + public WindowCfg(boolean aIsShown, int aPosX, int aPosY, int aDimX, int aDimY) + { + isShown = aIsShown; + + posX = aPosX; + posY = aPosY; + + dimX = aDimX; + dimY = aDimY; + } + + /** Serialization Constructor */ + public WindowCfg(ZinStream aStream) throws IOException + { + aStream.readVersion(0); + + isShown = aStream.readBool(); + + posX = aStream.readInt(); + posY = aStream.readInt(); + + dimX = aStream.readInt(); + dimY = aStream.readInt(); + } + + /** + * UI based constructor + * + * The WindowInfo will be setup to the current state of the provided {@link Component}. + */ + public WindowCfg(Component aComponent) + { + isShown = aComponent.isVisible(); + + posX = aComponent.getLocation().x; + posY = aComponent.getLocation().y; + + dimX = aComponent.getSize().width; + dimY = aComponent.getSize().height; + } + + /** + * Syncs the specified {@link Component} to match the state of this {@link WindowCfg}. + */ + public void configure(Component aComponent) + { + aComponent.setLocation(posX, posY); + + aComponent.setPreferredSize(new Dimension(dimX, dimY)); + aComponent.setSize(dimX, dimY); + } + + /** + * Returns a {@link WindowCfg} which matches this WindowInfo but with the isShown flag set to the requested setting. + */ + public WindowCfg withIsShown(boolean aIsShown) + { + // Bail if nothing changes + if (isShown == aIsShown) + return this; + + return new WindowCfg(aIsShown, posX, posY, dimX, dimY); + } + + // @formatter:off + // Accessor methods + public boolean isShown() { return isShown; } + public int dimX() { return dimX; } + public int dimY() { return dimY; } + public int posX() { return posX; } + public int posY() { return posY; } + // @formatter:on + + @Override + public void zioWrite(ZoutStream aStream) throws IOException + { + aStream.writeVersion(0); + + aStream.writeBool(isShown); + + aStream.writeInt(posX); + aStream.writeInt(posY); + + aStream.writeInt(dimX); + aStream.writeInt(dimY); + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + dimX; + result = prime * result + dimY; + result = prime * result + (isShown ? 1231 : 1237); + result = prime * result + posX; + result = prime * result + posY; + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + WindowCfg other = (WindowCfg) obj; + if (dimX != other.dimX) + return false; + if (dimY != other.dimY) + return false; + if (isShown != other.isShown) + return false; + if (posX != other.posX) + return false; + if (posY != other.posY) + return false; + return true; + } + +} diff --git a/src/glum/gui/info/WindowInfo.java b/src/glum/gui/info/WindowInfo.java deleted file mode 100644 index 9247252..0000000 --- a/src/glum/gui/info/WindowInfo.java +++ /dev/null @@ -1,77 +0,0 @@ -package glum.gui.info; - -import glum.zio.*; -import glum.zio.util.ZioUtil; - -import java.awt.*; -import java.io.*; - -public class WindowInfo implements ZioObj -{ - // Raw vars - protected Point position; - protected Dimension size; - protected boolean isVisible; - - /** - * Constructor - */ - public WindowInfo() - { - position = null; - size = null; - isVisible = false; - } - - public WindowInfo(Component aComponent) - { - this(); - - if (aComponent == null) - return; - - position = aComponent.getLocation(); - size = aComponent.getSize(); - isVisible = aComponent.isVisible(); - } - - /** - * configure - Syncs aComponent with parmaters of this WindowInfo - */ - public void configure(Component aComponent) - { - if (position != null) - aComponent.setLocation(position); - - if (size != null) - { - aComponent.setPreferredSize(size); - aComponent.setSize(size); - } - } - - @Override - public void zioRead(ZinStream aStream) throws IOException - { - aStream.readVersion(0); - - isVisible = aStream.readBool(); - - position = ZioUtil.readPoint(aStream); - - size = ZioUtil.readDimension(aStream); - } - - @Override - public void zioWrite(ZoutStream aStream) throws IOException - { - aStream.writeVersion(0); - - aStream.writeBool(isVisible); - - ZioUtil.writePoint(aStream, position); - - ZioUtil.writeDimension(aStream, size); - } - -} diff --git a/src/glum/gui/dialog/MemoryUtilDialog.java b/src/glum/gui/memory/MemoryUtilDialog.java similarity index 59% rename from src/glum/gui/dialog/MemoryUtilDialog.java rename to src/glum/gui/memory/MemoryUtilDialog.java index 968e548..7e000b3 100644 --- a/src/glum/gui/dialog/MemoryUtilDialog.java +++ b/src/glum/gui/memory/MemoryUtilDialog.java @@ -1,20 +1,36 @@ -package glum.gui.dialog; +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.memory; -import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; + import javax.swing.*; -import net.miginfocom.swing.MigLayout; import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.component.GLabel; -import glum.unit.ConstUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.UnitProvider; +import glum.unit.*; +import net.miginfocom.swing.MigLayout; +/** + * UI componet that provides a view of the applications memory state. + * + * @author lopeznr1 + */ public class MemoryUtilDialog extends JDialog implements ActionListener { // Gui components @@ -23,7 +39,7 @@ public class MemoryUtilDialog extends JDialog implements ActionListener private JButton closeB, gcRunB, updateB; /** - * Constructor + * Standard Constructor */ public MemoryUtilDialog(JFrame parentFrame) { @@ -33,8 +49,7 @@ public class MemoryUtilDialog extends JDialog implements ActionListener setDefaultCloseOperation(HIDE_ON_CLOSE); setModal(false); - DecimalFormat numFormat; - numFormat = new DecimalFormat(); + var numFormat = new DecimalFormat(); numFormat.setGroupingUsed(true); numFormat.setGroupingSize(3); numFormat.setMaximumFractionDigits(0); @@ -52,9 +67,7 @@ public class MemoryUtilDialog extends JDialog implements ActionListener @Override public void actionPerformed(ActionEvent e) { - Object source; - - source = e.getSource(); + var source = e.getSource(); if (source == gcRunB) { System.gc(); @@ -82,40 +95,36 @@ public class MemoryUtilDialog extends JDialog implements ActionListener */ private void buildGuiArea() { - JPanel aPanel; - JLabel tmpL; - Font tmpFont; - // Form the panel - aPanel = new JPanel(new MigLayout("", "[right][left,grow]", "[][][]10[]10")); - tmpFont = new JTextField().getFont(); + var tmpPanel = new JPanel(new MigLayout("", "[right][left,grow]", "[][][]10[]10")); + var tmpFont = new JTextField().getFont(); // Info area - tmpL = new JLabel("Total Memory:"); + var tmpL = new JLabel("Total Memory:"); totalMemL = new GLabel(byteUP, tmpFont, true); - aPanel.add(tmpL, ""); - aPanel.add(totalMemL, "growx,wrap"); + tmpPanel.add(tmpL, ""); + tmpPanel.add(totalMemL, "growx,wrap"); tmpL = new JLabel("Free Memory:"); freeMemL = new GLabel(byteUP, tmpFont, true); - aPanel.add(tmpL, ""); - aPanel.add(freeMemL, "growx,wrap"); + tmpPanel.add(tmpL, ""); + tmpPanel.add(freeMemL, "growx,wrap"); tmpL = new JLabel("Used Memory:"); usedMemL = new GLabel(byteUP, tmpFont, true); - aPanel.add(tmpL, ""); - aPanel.add(usedMemL, "growx,wrap"); + tmpPanel.add(tmpL, ""); + tmpPanel.add(usedMemL, "growx,wrap"); // Control area updateB = GuiUtil.createJButton("Update", this); gcRunB = GuiUtil.createJButton("Run GC", this); closeB = GuiUtil.createJButton("Close", this); - aPanel.add(updateB, "span 2,split 3"); - aPanel.add(gcRunB, ""); - aPanel.add(closeB, ""); + tmpPanel.add(updateB, "span 2,split 3"); + tmpPanel.add(gcRunB, ""); + tmpPanel.add(closeB, ""); // Add the main panel into the dialog - getContentPane().add(aPanel); + getContentPane().add(tmpPanel); pack(); } @@ -124,15 +133,12 @@ public class MemoryUtilDialog extends JDialog implements ActionListener */ private void updateGui() { - Runtime aRuntime; - long freeMem, usedMem, totalMem; - - aRuntime = Runtime.getRuntime(); + var tmpRuntime = Runtime.getRuntime(); // Update the memory usage - freeMem = aRuntime.freeMemory(); - totalMem = aRuntime.totalMemory(); - usedMem = totalMem - freeMem; + var freeMem = tmpRuntime.freeMemory(); + var totalMem = tmpRuntime.totalMemory(); + var usedMem = totalMem - freeMem; freeMemL.setValue(freeMem); totalMemL.setValue(totalMem); diff --git a/src/glum/gui/misc/BooleanCellEditor.java b/src/glum/gui/misc/BooleanCellEditor.java index 334bc16..bc3d80a 100644 --- a/src/glum/gui/misc/BooleanCellEditor.java +++ b/src/glum/gui/misc/BooleanCellEditor.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.Component; @@ -5,43 +18,47 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Collection; import java.util.LinkedList; -import javax.swing.AbstractCellEditor; -import javax.swing.JCheckBox; -import javax.swing.JTable; + +import javax.swing.*; import javax.swing.table.TableCellEditor; +/** + * Cell editor suitable for editing boolean values. + * + * @author lopeznr1 + */ public class BooleanCellEditor extends AbstractCellEditor implements ActionListener, TableCellEditor { // State vars - protected Collection myListeners; - protected JCheckBox refCheckBox; + private Collection listenerL; + private JCheckBox refCheckBox; - /** - * Constructor - */ + /** Standard Constructor */ + public BooleanCellEditor(ActionListener aListener) + { + listenerL = new LinkedList<>(); + if (aListener != null) + listenerL.add(aListener); + + refCheckBox = new JCheckBox("", false); + refCheckBox.addActionListener(this); + refCheckBox.setHorizontalAlignment(JCheckBox.CENTER); + } + + /** Simplified Constructor */ public BooleanCellEditor() { this(null); } - public BooleanCellEditor(ActionListener aListener) - { - myListeners = new LinkedList(); - if (aListener != null) - myListeners.add(aListener); - - refCheckBox = new JCheckBox("", false); - refCheckBox.addActionListener(this); - } - public void addActionListener(ActionListener aListener) { - myListeners.add(aListener); + listenerL.add(aListener); } public void removeActionListener(ActionListener aListener) { - myListeners.remove(aListener); + listenerL.remove(aListener); } @Override @@ -50,7 +67,7 @@ public class BooleanCellEditor extends AbstractCellEditor implements ActionListe fireEditingStopped(); aEvent = new ActionEvent(this, aEvent.getID(), "BooleanCell edited."); - for (ActionListener aListener : myListeners) + for (ActionListener aListener : listenerL) aListener.actionPerformed(aEvent); } @@ -60,7 +77,7 @@ public class BooleanCellEditor extends AbstractCellEditor implements ActionListe // Update our checkbox with the appropriate state refCheckBox.removeActionListener(this); if (value instanceof Boolean) - refCheckBox.setSelected((Boolean)value); + refCheckBox.setSelected((Boolean) value); refCheckBox.addActionListener(this); return refCheckBox; diff --git a/src/glum/gui/misc/BooleanCellRenderer.java b/src/glum/gui/misc/BooleanCellRenderer.java index 30a5062..dd5b167 100644 --- a/src/glum/gui/misc/BooleanCellRenderer.java +++ b/src/glum/gui/misc/BooleanCellRenderer.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.Component; @@ -6,19 +19,27 @@ import javax.swing.JCheckBox; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; +/** + * {@link TableCellRenderer} used to render boolean values. + * + * @author lopeznr1 + */ public class BooleanCellRenderer extends JCheckBox implements TableCellRenderer { + /** Standard Constructor */ public BooleanCellRenderer() { super("", false); + setHorizontalAlignment(JCheckBox.CENTER); } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { if (value instanceof Boolean) { - setSelected((Boolean)value); + setSelected((Boolean) value); return this; } diff --git a/src/glum/gui/misc/ColorCellRenderer.java b/src/glum/gui/misc/ColorCellRenderer.java index 2d1ede7..a2440ad 100644 --- a/src/glum/gui/misc/ColorCellRenderer.java +++ b/src/glum/gui/misc/ColorCellRenderer.java @@ -1,35 +1,47 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; -import java.awt.Color; -import java.awt.Component; -import java.awt.Graphics; -import java.awt.Graphics2D; +import java.awt.*; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.table.TableCellRenderer; +/** + * {@link TableCellRenderer} used to render a color. + * + * @author lopeznr1 + */ public class ColorCellRenderer extends JPanel implements TableCellRenderer { // State vars protected Color activeColor; - /** - * Constructor - */ + /** Standard Constructor */ public ColorCellRenderer() { - super(); - activeColor = null; } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { activeColor = null; if (value instanceof Color) - activeColor = (Color)value; + activeColor = (Color) value; if (activeColor != null) setBackground(activeColor); @@ -42,10 +54,8 @@ public class ColorCellRenderer extends JPanel implements TableCellRenderer @Override public void paint(Graphics g) { - Graphics2D g2d; - super.paint(g); - g2d = (Graphics2D)g; + var g2d = (Graphics2D) g; // Bail if we have a valid color if (activeColor != null) diff --git a/src/glum/gui/misc/CustomLCR.java b/src/glum/gui/misc/CustomLCR.java new file mode 100644 index 0000000..f8a1fd3 --- /dev/null +++ b/src/glum/gui/misc/CustomLCR.java @@ -0,0 +1,79 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.misc; + +import java.awt.Component; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.*; + +/** + * {@link ListCellRenderer} used to render custom labels corresponding to the provided items. + *

    + * If an item is not registered via {@link #addMapping(Object, String)}, then the string returned by + * {@link Object#toString()} will be utilized instead. + * + * @author lopeznr1 + */ +public class CustomLCR extends DefaultListCellRenderer +{ + // State vars + private final Map labelM; + + /** Standard Constructor */ + public CustomLCR() + { + labelM = new HashMap<>(); + } + + @Override + public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, + boolean hasFocus) + { + JLabel retL = (JLabel) super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); + + String tmpLabel = labelM.get(aObj); + if (tmpLabel == null) + tmpLabel = "" + aObj; + + retL.setText(tmpLabel); + return retL; + } + + /** + * Registers the label that will be shown when the specified item is selected. + */ + public void addMapping(Object aItem, String aLabel) + { + labelM.put(aItem, aLabel); + } + + /** + * Deregisters the label that will be shown when the specified item is selected. + */ + public void delMapping(Object aItem) + { + labelM.remove(aItem); + } + + /** + * Returns the label associated with the specified mapping. + */ + public String getLabel(Object aItem) + { + return labelM.get(aItem); + } + +} diff --git a/src/glum/gui/misc/MultiState.java b/src/glum/gui/misc/MultiState.java index 4161fcd..1ea064a 100644 --- a/src/glum/gui/misc/MultiState.java +++ b/src/glum/gui/misc/MultiState.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.*; diff --git a/src/glum/gui/misc/MultiStateCheckBox.java b/src/glum/gui/misc/MultiStateCheckBox.java index 428d94e..c632d6f 100644 --- a/src/glum/gui/misc/MultiStateCheckBox.java +++ b/src/glum/gui/misc/MultiStateCheckBox.java @@ -1,9 +1,23 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.event.*; -import java.util.*; +import java.util.HashMap; + import javax.swing.*; -import javax.swing.plaf.*; +import javax.swing.plaf.ActionMapUIResource; public class MultiStateCheckBox extends JCheckBox implements MouseListener { @@ -12,7 +26,7 @@ public class MultiStateCheckBox extends JCheckBox implements MouseListener protected HashMap nextStateMap; /** - * Constructor + * Standard Constructor */ public MultiStateCheckBox(String text, boolean is3StateCycle) { @@ -76,10 +90,8 @@ public class MultiStateCheckBox extends JCheckBox implements MouseListener @Override public void doClick() { - MouseEvent aEvent; - - aEvent = new MouseEvent(this, MouseEvent.MOUSE_PRESSED, 0, 0, 0, 0, 0, false); - handleMouseEvent(aEvent); + var tmpEvent = new MouseEvent(this, MouseEvent.MOUSE_PRESSED, 0, 0, 0, 0, 0, false); + handleMouseEvent(tmpEvent); model.advanceToNextState(); } @@ -138,25 +150,23 @@ public class MultiStateCheckBox extends JCheckBox implements MouseListener */ protected void handleMouseEvent(MouseEvent e) { - int aID; - - aID = e.getID(); - if (aID == MouseEvent.MOUSE_ENTERED) + var tmpID = e.getID(); + if (tmpID == MouseEvent.MOUSE_ENTERED) { model.setArmed(true); } - else if (aID == MouseEvent.MOUSE_EXITED) + else if (tmpID == MouseEvent.MOUSE_EXITED) { model.setArmed(false); } - else if (aID == MouseEvent.MOUSE_RELEASED) + else if (tmpID == MouseEvent.MOUSE_RELEASED) { if (model.isArmed() == true) model.advanceToNextState(); model.setPressed(false); } - else if (aID == MouseEvent.MOUSE_PRESSED) + else if (tmpID == MouseEvent.MOUSE_PRESSED) { grabFocus(); model.setPressed(true); @@ -168,10 +178,7 @@ public class MultiStateCheckBox extends JCheckBox implements MouseListener */ protected void rebuildKeyboardMap() { - ActionMap map; - - map = new ActionMapUIResource(); - + var map = new ActionMapUIResource(); map.put("pressed", new AbstractAction() { @Override diff --git a/src/glum/gui/misc/MultiStateCheckBoxCellEditor.java b/src/glum/gui/misc/MultiStateCheckBoxCellEditor.java index f0cd124..1c79033 100644 --- a/src/glum/gui/misc/MultiStateCheckBoxCellEditor.java +++ b/src/glum/gui/misc/MultiStateCheckBoxCellEditor.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.Component; diff --git a/src/glum/gui/misc/MultiStateCheckBoxCellRenderer.java b/src/glum/gui/misc/MultiStateCheckBoxCellRenderer.java index 2aa4e4f..d37e5f0 100644 --- a/src/glum/gui/misc/MultiStateCheckBoxCellRenderer.java +++ b/src/glum/gui/misc/MultiStateCheckBoxCellRenderer.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.Component; diff --git a/src/glum/gui/misc/MultiStateCheckBoxHeader.java b/src/glum/gui/misc/MultiStateCheckBoxHeader.java index 4f7ef92..8655c68 100644 --- a/src/glum/gui/misc/MultiStateCheckBoxHeader.java +++ b/src/glum/gui/misc/MultiStateCheckBoxHeader.java @@ -1,18 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; -import java.awt.*; +import java.awt.Component; import java.awt.event.*; -import javax.swing.*; -import javax.swing.table.*; + +import javax.swing.JTable; +import javax.swing.UIManager; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellRenderer; public class MultiStateCheckBoxHeader extends MultiStateCheckBox implements TableCellRenderer, MouseListener, MouseMotionListener { // State vars - protected JTableHeader refHeader; - protected int column; + private JTableHeader refHeader; + private int column; /** - * Constructor + * Standard Constructor */ public MultiStateCheckBoxHeader(JTable aTable, boolean is3StateCycle) { @@ -36,19 +52,14 @@ public class MultiStateCheckBoxHeader extends MultiStateCheckBox implements Tabl */ public int getAssociatedColumn(MouseEvent aEvent) { - JTableHeader aHeader; - JTable aTable; - TableColumnModel aColumnModel; - int viewCol, refCol; - if (aEvent.getSource() instanceof JTableHeader == false) return -1; - aHeader = (JTableHeader)aEvent.getSource(); - aTable = aHeader.getTable(); - aColumnModel = aTable.getColumnModel(); - viewCol = aColumnModel.getColumnIndexAtX(aEvent.getX()); - refCol = aTable.convertColumnIndexToModel(viewCol); + var tmpHeader = (JTableHeader)aEvent.getSource(); + var tmpTable = tmpHeader.getTable(); + var tmpColumnModel = tmpTable.getColumnModel(); + var viewCol = tmpColumnModel.getColumnIndexAtX(aEvent.getX()); + var refCol = tmpTable.convertColumnIndexToModel(viewCol); return viewCol; } diff --git a/src/glum/gui/misc/MultiStateCheckBoxHeaderTest.java b/src/glum/gui/misc/MultiStateCheckBoxHeaderTest.java index d701de4..e0c8807 100644 --- a/src/glum/gui/misc/MultiStateCheckBoxHeaderTest.java +++ b/src/glum/gui/misc/MultiStateCheckBoxHeaderTest.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.event.*; diff --git a/src/glum/gui/misc/MultiStateCheckBoxTest.java b/src/glum/gui/misc/MultiStateCheckBoxTest.java index 089acb2..4c24923 100644 --- a/src/glum/gui/misc/MultiStateCheckBoxTest.java +++ b/src/glum/gui/misc/MultiStateCheckBoxTest.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.*; diff --git a/src/glum/gui/misc/MultiStateIcon.java b/src/glum/gui/misc/MultiStateIcon.java index 14a5b2d..5ef47be 100644 --- a/src/glum/gui/misc/MultiStateIcon.java +++ b/src/glum/gui/misc/MultiStateIcon.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.*; diff --git a/src/glum/gui/misc/MultiStateModel.java b/src/glum/gui/misc/MultiStateModel.java index 888a91c..20043e3 100644 --- a/src/glum/gui/misc/MultiStateModel.java +++ b/src/glum/gui/misc/MultiStateModel.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.event.*; diff --git a/src/glum/gui/misc/SimpleTitledBorder.java b/src/glum/gui/misc/SimpleTitledBorder.java index 9e4c3da..9b999f6 100644 --- a/src/glum/gui/misc/SimpleTitledBorder.java +++ b/src/glum/gui/misc/SimpleTitledBorder.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.misc; import java.awt.*; diff --git a/src/glum/gui/panel/CardPanel.java b/src/glum/gui/panel/CardPanel.java index 73531e3..10793ff 100644 --- a/src/glum/gui/panel/CardPanel.java +++ b/src/glum/gui/panel/CardPanel.java @@ -1,40 +1,54 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import java.awt.*; +import java.awt.CardLayout; +import java.awt.Component; import java.util.*; -import javax.swing.*; - -import com.google.common.collect.*; +import javax.swing.JPanel; /** * Panel that allows you to shuffle between a collection of "card" panels. Each card must be a descendant of JPanel. - *

    - * Unlike CardLayout this class supports mapping of an object (rather than a string) to a card. + *

    + * Unlike {@link CardLayout} this class supports mapping of an object (rather than a string) to a card. + * + * @author lopeznr1 */ public class CardPanel extends JPanel { - protected BiMap keyMap; - protected Map revMap; - protected CardLayout myLayout; - protected G1 activeCard; - protected G1 backupCard; + // State vars + private HashMap keyMap; + private Map revMap; + private CardLayout workCardLayout; + private G1 activeCard; + private G1 backupCard; + /** Standard Constructor */ public CardPanel() { - super(); + workCardLayout = new CardLayout(); + setLayout(workCardLayout); - myLayout = new CardLayout(); - setLayout(myLayout); - - keyMap = HashBiMap.create(); + keyMap = new HashMap<>(); revMap = new HashMap<>(); activeCard = null; backupCard = null; } @Override - public Component add(String name, Component comp) + public Component add(String aName, Component aComp) { throw new RuntimeException("Improper method call. Use addCard() instead of add()"); } @@ -42,29 +56,26 @@ public class CardPanel extends JPanel /** * Adds and associates the key, aKey with the specified card aCard. */ - public void addCard(Object aKey, G1 aCard) { // aCard must be of type Component if ((aCard instanceof Component) == false) - throw new IllegalArgumentException("aCard must be of type Component. Found class: " + aCard.getClass().getName()); + throw new IllegalArgumentException( + "aCard must be of type Component. Found class: " + aCard.getClass().getName()); - // Add the card if no card associated with the key + // Associate the card with the specified key if (keyMap.get(aKey) == null) - { - // Form the 2-way association of aKey and aComponent keyMap.put(aKey, aCard); - - // Form the 1-way mapping of aComponent to a (unique) strKey - String strKey = "" + revMap.size(); - revMap.put(aCard, strKey); - - add((Component)aCard, strKey); - } // If the key is associated, then ensure it is matched to aCard else if (keyMap.get(aKey) != aCard) - { throw new RuntimeException("Attempting to add new card with an already inserted key: " + aKey); + + // Form a mapping of aCard to a unique string key + if (revMap.containsKey(aCard) == false) + { + String strKey = "" + revMap.size(); + revMap.put(aCard, strKey); + add((Component) aCard, strKey); } switchToCard(aKey); @@ -78,17 +89,23 @@ public class CardPanel extends JPanel return activeCard; } - public Collection getAllCards() + public List getAllCards() { - Collection itemList; + var retCardL = new ArrayList(keyMap.values()); + return retCardL; + } - itemList = new ArrayList(keyMap.values()); - return itemList; + /** + * Returns the card associated with the specified key. + */ + public G1 getCardForKey(Object aKey) + { + return keyMap.get(aKey); } /** * Method to specify the backup card to use should no card be found that corresponds to the specified key. - *

    + *

    * This method will throw a RuntimeException if a card has not been associated with the specified key. */ public void setBackupCard(Object aKey) @@ -100,11 +117,13 @@ public class CardPanel extends JPanel /** * Causes this CardPanel to show the card associated with the specified key. - *

    - * A RuntimeException will be thrown if there is no card associated with aKey and the backupCord has not been specified. + *

    + * A RuntimeException will be thrown if there is no card associated with aKey and the backupCord has not been + * specified. */ public void switchToCard(Object aKey) { + // Switch to the proper card activeCard = keyMap.get(aKey); if (activeCard == null) activeCard = backupCard; @@ -113,22 +132,9 @@ public class CardPanel extends JPanel if (activeCard == null) throw new RuntimeException("No mapping found when switching to card: " + aKey); - switchToInstalledCard(activeCard); - } - - /** - * Causes this CardPanel to show the specified card. Throws a RuntimeException if the card was not previously added via addCard(). - */ - public void switchToInstalledCard(G1 aCard) - { - if (keyMap.values().contains(aCard) == false) - throw new RuntimeException("No mapping found when switching to card: " + aCard); - - // Switch to the proper card - activeCard = aCard; - - String strKey = revMap.get(aCard); - myLayout.show(this, strKey); + // Switch the CardLayout to the activeCard + String strKey = revMap.get(activeCard); + workCardLayout.show(this, strKey); } } diff --git a/src/glum/gui/panel/ColorInputPanel.java b/src/glum/gui/panel/ColorInputPanel.java index d6313c1..927e287 100644 --- a/src/glum/gui/panel/ColorInputPanel.java +++ b/src/glum/gui/panel/ColorInputPanel.java @@ -1,10 +1,19 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.gui.component.GNumberField; -import glum.unit.*; - import java.awt.Color; -import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -12,26 +21,31 @@ import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import com.google.common.collect.Range; + +import glum.gui.component.GNumberField; +import glum.unit.ConstUnitProvider; +import glum.unit.NumberUnit; import net.miginfocom.swing.MigLayout; +/** + * User input component that allows the user to specify the a {@link Color}. + * + * @author lopeznr1 + */ public class ColorInputPanel extends GPanel implements ActionListener, ChangeListener { - // Constants - private static final Font miniFont = new Font("Serif", Font.PLAIN, 10); - // Gui components private ColorPanel colorP; - private JLabel redL, greenL, blueL; - private JSlider redS, greenS, blueS; - private GNumberField redNF, greenNF, blueNF; + private JLabel redL, greenL, blueL, alphaL; + private JSlider redS, greenS, blueS, alphaS; + private GNumberField redNF, greenNF, blueNF, alphaNF; - /** - * Constructor - */ - public ColorInputPanel(boolean isHorizontal, boolean showTF) + /** Standard Constructor */ + public ColorInputPanel(boolean aIsHorizontal, boolean aShowTF, boolean aShowAlpha) { // Build the gui areas - buildGuiArea(isHorizontal, showTF); + buildGuiArea(aShowTF, aIsHorizontal, aShowAlpha); // Set in the default color setColorConfig(Color.BLACK); @@ -42,12 +56,11 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis */ public Color getColorConfig() { - int redVal, greenVal, blueVal; - - redVal = redS.getValue(); - greenVal = greenS.getValue(); - blueVal = blueS.getValue(); - return new Color(redVal, greenVal, blueVal); + var redVal = redS.getValue(); + var greenVal = greenS.getValue(); + var blueVal = blueS.getValue(); + var alphaVal = alphaS.getValue(); + return new Color(redVal, greenVal, blueVal, alphaVal); } /** @@ -65,14 +78,12 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - // Perform GUI updates - source = aEvent.getSource(); + var source = aEvent.getSource(); updateGui(source); // Notify the listeners - notifyListeners(source, ID_UPDATE); + notifyListeners(this, ID_UPDATE); } @Override @@ -87,6 +98,9 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis blueL.setEnabled(aBool); blueS.setEnabled(aBool); blueNF.setEnabled(aBool); + alphaL.setEnabled(aBool); + alphaS.setEnabled(aBool); + alphaNF.setEnabled(aBool); colorP.setEnabled(aBool); } @@ -94,97 +108,106 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis @Override public void stateChanged(ChangeEvent aEvent) { - Object source; - JSlider aSlider; - // Perform GUI updates - source = aEvent.getSource(); + var source = aEvent.getSource(); updateGui(source); // Notify the listeners if (source instanceof JSlider) { // Fire off an event only if not being updated - aSlider = (JSlider)source; - if (aSlider.getValueIsAdjusting() == false) - notifyListeners(source, ID_UPDATE); + var tmpSlider = (JSlider) source; + if (tmpSlider.getValueIsAdjusting() == false) + notifyListeners(this, ID_UPDATE); else - notifyListeners(source, ID_UPDATING); + notifyListeners(this, ID_UPDATING); } } /** * Forms the actual gui */ - private void buildGuiArea(boolean isHorizontal, boolean showTF) + private void buildGuiArea(boolean aIsHorizontal, boolean aShowTF, boolean aShowAlpha) { - JPanel rPanel, gPanel, bPanel; - UnitProvider countUP; - int sliderStyle; - - sliderStyle = JSlider.HORIZONTAL; - if (isHorizontal == false) + var sliderStyle = JSlider.HORIZONTAL; + if (aIsHorizontal == false) sliderStyle = JSlider.VERTICAL; - countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, 0)); + var countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, 0)); // RGB sliders + var rgbRange = Range.closed(0.0, 255.0); + redL = new JLabel("R", JLabel.CENTER); redS = new JSlider(sliderStyle, 0, 255, 0); - redNF = new GNumberField(this, countUP, 0, 255); - rPanel = formColorControl(redS, redL, redNF, isHorizontal, showTF); + redNF = new GNumberField(this, countUP, rgbRange); + var rPanel = formColorControl(redS, redL, redNF, aIsHorizontal, aShowTF); greenL = new JLabel("G", JLabel.CENTER); greenS = new JSlider(sliderStyle, 0, 255, 0); - greenNF = new GNumberField(this, countUP, 0, 255); - gPanel = formColorControl(greenS, greenL, greenNF, isHorizontal, showTF); + greenNF = new GNumberField(this, countUP, rgbRange); + var gPanel = formColorControl(greenS, greenL, greenNF, aIsHorizontal, aShowTF); blueL = new JLabel("B", JLabel.CENTER); blueS = new JSlider(sliderStyle, 0, 255, 0); - blueNF = new GNumberField(this, countUP, 0, 255); - bPanel = formColorControl(blueS, blueL, blueNF, isHorizontal, showTF); + blueNF = new GNumberField(this, countUP, rgbRange); + var bPanel = formColorControl(blueS, blueL, blueNF, aIsHorizontal, aShowTF); + + alphaL = new JLabel("A", JLabel.CENTER); + alphaS = new JSlider(sliderStyle, 0, 255, 0); + alphaNF = new GNumberField(this, countUP, rgbRange); + var aPanel = formColorControl(alphaS, alphaL, alphaNF, aIsHorizontal, aShowTF); // The color area colorP = new ColorPanel(40, 40); + colorP.setOpaque(true); - if (isHorizontal == true) + if (aIsHorizontal == true) { - setLayout(new MigLayout("", "0[grow,75::][]0", "0[][][]0")); + if (aShowAlpha == false) + setLayout(new MigLayout("", "0[grow,75::][]0", "0[][][]0")); + else + setLayout(new MigLayout("", "0[grow,75::][]0", "0[][][][]0")); - add(rPanel, "growx,span 1,wrap"); - add(gPanel, "growx,span 1,wrap"); - add(bPanel, "growx,span 1,wrap"); - add(colorP, "cell 1 0,growy,spanx 1,spany 3"); + add(rPanel, "growx,wrap"); + add(gPanel, "growx,wrap"); + add(bPanel, "growx,wrap"); + if (aShowAlpha == true) + add(aPanel, "growx,wrap"); + add(colorP, "cell 1 0,growy,spany"); } else { - setLayout(new MigLayout("", "0[][][]0", "0[grow,75::][]0")); + if (aShowAlpha == false) + setLayout(new MigLayout("", "0[][][]0", "0[grow,75::][]0")); + else + setLayout(new MigLayout("", "0[][][][]0", "0[grow,75::][]0")); - add(rPanel, "growy,span 1"); - add(gPanel, "growy,span 1"); - add(bPanel, "growy,span 1,wrap"); - add(colorP, "growx,span 3"); + add(rPanel, "growy"); + add(gPanel, "growy"); + add(bPanel, "growy"); + if (aShowAlpha == true) + add(aPanel, "growy"); + add(colorP, "newline,growx,spanx"); } } /** * builds a JSlider with a label */ - private JPanel formColorControl(JSlider aS, JLabel aL, GNumberField aNF, boolean isHorizontal, boolean showTF) + private JPanel formColorControl(JSlider aS, JLabel aL, GNumberField aNF, boolean aIsHorizontal, boolean aShowTF) { - JPanel aPanel; - - aPanel = new JPanel(); - if (isHorizontal == true) + var retPanel = new JPanel(); + if (aIsHorizontal == true) { - aPanel.setLayout(new BoxLayout(aPanel, BoxLayout.X_AXIS)); + retPanel.setLayout(new BoxLayout(retPanel, BoxLayout.X_AXIS)); aL.setAlignmentY(0.5f); aS.setAlignmentY(0.5f); aNF.setAlignmentY(0.5f); } else { - aPanel.setLayout(new BoxLayout(aPanel, BoxLayout.Y_AXIS)); + retPanel.setLayout(new BoxLayout(retPanel, BoxLayout.Y_AXIS)); aL.setAlignmentX(0.5f); aS.setAlignmentX(0.5f); aNF.setAlignmentX(0.5f); @@ -195,22 +218,21 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis aNF.setHorizontalAlignment(JTextField.CENTER); aNF.setColumns(3); aNF.setValue(0); - aNF.setFont(miniFont); aNF.setMinimumSize(aNF.getPreferredSize()); aNF.setMaximumSize(aNF.getPreferredSize()); - aPanel.add(aL); - aPanel.add(aS); + retPanel.add(aL); + retPanel.add(aS); - if (isHorizontal == true) - aPanel.add(Box.createHorizontalStrut(2)); + if (aIsHorizontal == true) + retPanel.add(Box.createHorizontalStrut(2)); else - aPanel.add(Box.createVerticalStrut(2)); + retPanel.add(Box.createVerticalStrut(2)); - if (showTF == true) - aPanel.add(aNF); + if (aShowTF == true) + retPanel.add(aNF); - return aPanel; + return retPanel; } /** @@ -218,17 +240,17 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis */ private void synchronizeGui(Color aColor) { - int redVal, greenVal, blueVal; - // Get the rgb values - redVal = aColor.getRed(); - greenVal = aColor.getGreen(); - blueVal = aColor.getBlue(); + var redVal = aColor.getRed(); + var greenVal = aColor.getGreen(); + var blueVal = aColor.getBlue(); + var alphaVal = aColor.getAlpha(); // Stop listening to events while updating redS.removeChangeListener(this); greenS.removeChangeListener(this); blueS.removeChangeListener(this); + alphaS.removeChangeListener(this); // Update the gui components if (redVal != redNF.getValue()) @@ -237,30 +259,34 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis greenNF.setValue(greenVal); if (blueVal != blueNF.getValue()) blueNF.setValue(blueVal); + if (alphaVal != alphaNF.getValue()) + alphaNF.setValue(alphaVal); redS.setValue(redVal); greenS.setValue(greenVal); blueS.setValue(blueVal); - colorP.setColor(new Color(redVal, greenVal, blueVal)); + alphaS.setValue(alphaVal); + colorP.setColor(new Color(redVal, greenVal, blueVal, 255)); // Proceed with listening to events redS.addChangeListener(this); greenS.addChangeListener(this); blueS.addChangeListener(this); + alphaS.addChangeListener(this); } /** * Updates the gui to reflect the source that has changed */ - private void updateGui(Object source) + private void updateGui(Object aSource) { - int redVal, greenVal, blueVal; - // Determine what values to retrieve based on the source - if (source instanceof GNumberField) + int redVal, greenVal, blueVal, alphaVal; + if (aSource instanceof GNumberField) { redVal = redNF.getValueAsInt(0); greenVal = greenNF.getValueAsInt(0); blueVal = blueNF.getValueAsInt(0); + alphaVal = alphaNF.getValueAsInt(0); } else { @@ -268,42 +294,53 @@ public class ColorInputPanel extends GPanel implements ActionListener, ChangeLis redVal = redS.getValue(); greenVal = greenS.getValue(); blueVal = blueS.getValue(); + alphaVal = alphaS.getValue(); } // Update the appropriate component - if (source == redS) + if (aSource == redS) { redNF.setValue(redVal); } - else if (source == greenS) + else if (aSource == greenS) { greenNF.setValue(greenVal); } - else if (source == blueS) + else if (aSource == blueS) { blueNF.setValue(blueVal); } - else if (source == redNF) + else if (aSource == alphaS) + { + alphaNF.setValue(alphaVal); + } + else if (aSource == redNF) { redS.removeChangeListener(this); redS.setValue(redVal); redS.addChangeListener(this); } - else if (source == greenNF) + else if (aSource == greenNF) { greenS.removeChangeListener(this); greenS.setValue(greenVal); greenS.addChangeListener(this); } - else if (source == blueNF) + else if (aSource == blueNF) { blueS.removeChangeListener(this); blueS.setValue(blueVal); blueS.addChangeListener(this); } + else if (aSource == alphaNF) + { + alphaS.removeChangeListener(this); + alphaS.setValue(alphaVal); + alphaS.addChangeListener(this); + } // Update the preview color - colorP.setColor(new Color(redVal, greenVal, blueVal)); + colorP.setColor(new Color(redVal, greenVal, blueVal, 255)); } } diff --git a/src/glum/gui/panel/ColorPanel.java b/src/glum/gui/panel/ColorPanel.java index aa5eef1..4657fa5 100644 --- a/src/glum/gui/panel/ColorPanel.java +++ b/src/glum/gui/panel/ColorPanel.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; import java.awt.Color; @@ -10,12 +23,10 @@ import javax.swing.border.LineBorder; public class ColorPanel extends JPanel { // State vars - protected Color dispColor; + private Color dispColor; public ColorPanel() { - super(); - dispColor = Color.BLACK; updateGui(); } @@ -49,9 +60,7 @@ public class ColorPanel extends JPanel */ protected void updateGui() { - boolean isEnabled; - - isEnabled = isEnabled(); + var isEnabled = isEnabled(); if (isEnabled == false) { setBackground(Color.LIGHT_GRAY); diff --git a/src/glum/gui/panel/ComponentTracker.java b/src/glum/gui/panel/ComponentTracker.java index 7fb0dd8..8b4c656 100644 --- a/src/glum/gui/panel/ComponentTracker.java +++ b/src/glum/gui/panel/ComponentTracker.java @@ -1,24 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; import java.awt.Component; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; +import java.util.Arrays; import java.util.List; -import com.google.common.collect.Lists; - /** - * Utility class to allow a component to track other Component so it can keep it's - * properties synchronized with the tracked Components. + * Worker class to allow a component to track other Component so it can keep it's properties synchronized with the + * tracked Components. + * + * @author lopeznr1 */ public class ComponentTracker implements ComponentListener { - protected Component targComp; - protected Component trkHiddenComp; - protected Component trkMovedComp; - protected Component trkResizedComp; - protected Component trkShownComp; - + private Component targComp; + private Component trkHiddenComp; + private Component trkMovedComp; + private Component trkResizedComp; + private Component trkShownComp; + public ComponentTracker(Component aTargComp) { targComp = aTargComp; @@ -27,63 +41,59 @@ public class ComponentTracker implements ComponentListener trkResizedComp = null; trkShownComp = null; } - + /** - * Track aComp so that if it is hidden, then the reference - * targetComponent will be hidden. + * Track aComp so that if it is hidden, then the reference targetComponent will be hidden. */ public void setHiddenTracker(Component aComp) { // Deregister from the old trkShownComp if (trkHiddenComp != null) trkHiddenComp.removeComponentListener(this); - + trkHiddenComp = aComp; updateListenersForTrackedComponents(); } /** - * Track aComp so that if it is moved, then the reference - * targetComponent will be moved. + * Track aComp so that if it is moved, then the reference targetComponent will be moved. */ public void setMovedTracker(Component aComp) { // Deregister from the old trkShownComp if (trkMovedComp != null) trkMovedComp.removeComponentListener(this); - + trkMovedComp = aComp; updateListenersForTrackedComponents(); } /** - * Track aComp so that if it is resized, then the reference - * targetComponent will be resized. + * Track aComp so that if it is resized, then the reference targetComponent will be resized. */ public void setResizedTracker(Component aComp) { // Deregister from the old trkShownComp if (trkResizedComp != null) trkResizedComp.removeComponentListener(this); - + trkResizedComp = aComp; updateListenersForTrackedComponents(); } /** - * Track aComp so that if it is shown, then the reference - * targetComponent will be shown. + * Track aComp so that if it is shown, then the reference targetComponent will be shown. */ public void setShownTracker(Component aComp) { // Deregister from the old trkShownComp if (trkShownComp != null) trkShownComp.removeComponentListener(this); - + trkShownComp = aComp; updateListenersForTrackedComponents(); } - + @Override public void componentHidden(ComponentEvent aEvent) { @@ -114,43 +124,42 @@ public class ComponentTracker implements ComponentListener if (aEvent.getComponent() == trkShownComp) targComp.setVisible(true); } - + /** - * Utility method to ensure that we are registered (Component events) for - * all component which are being tracked. Note that at most we will register - * only only once for each unique Component. + * Utility method to ensure that we are registered (Component events) for all component which are being tracked. Note + * that at most we will register only only once for each unique Component. */ protected void updateListenersForTrackedComponents() { - List listenerList; + List listenerL; if (trkHiddenComp != null) { - listenerList = Lists.newArrayList(trkHiddenComp.getComponentListeners()); - if (listenerList.contains(this) == false) + listenerL = Arrays.asList(trkHiddenComp.getComponentListeners()); + if (listenerL.contains(this) == false) trkHiddenComp.addComponentListener(this); } - + if (trkMovedComp != null) { - listenerList = Lists.newArrayList(trkMovedComp.getComponentListeners()); - if (listenerList.contains(this) == false) + listenerL = Arrays.asList(trkMovedComp.getComponentListeners()); + if (listenerL.contains(this) == false) trkMovedComp.addComponentListener(this); } - + if (trkResizedComp != null) { - listenerList = Lists.newArrayList(trkResizedComp.getComponentListeners()); - if (listenerList.contains(this) == false) + listenerL = Arrays.asList(trkResizedComp.getComponentListeners()); + if (listenerL.contains(this) == false) trkResizedComp.addComponentListener(this); } - + if (trkShownComp != null) { - listenerList = Lists.newArrayList(trkShownComp.getComponentListeners()); - if (listenerList.contains(this) == false) + listenerL = Arrays.asList(trkShownComp.getComponentListeners()); + if (listenerL.contains(this) == false) trkShownComp.addComponentListener(this); } } - + } diff --git a/src/glum/gui/panel/CredentialPanel.java b/src/glum/gui/panel/CredentialPanel.java index 5b9dd89..a6fb21b 100644 --- a/src/glum/gui/panel/CredentialPanel.java +++ b/src/glum/gui/panel/CredentialPanel.java @@ -1,48 +1,56 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.net.Credential; -import glum.net.NetUtil; -import glum.net.Result; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.component.GPasswordField; import glum.gui.component.GTextField; -import glum.gui.panel.GlassPanel; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JPasswordField; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; - +import glum.net.*; import net.miginfocom.swing.MigLayout; +/** + * Panel that provides functionality that allow the user to enter credentials. + * + * @author lopeznr1 + */ public class CredentialPanel extends GlassPanel implements ActionListener { // Constants private static final Color warnColor = new Color(128, 0, 0); // GUI vars - protected JLabel titleL; - protected JButton ignoreB, acceptB; - protected JPasswordField passTF; - protected GTextField userTF; - protected JTextField sourceTF, warnTA; + private JLabel titleL; + private JButton ignoreB, acceptB; + private JPasswordField passTF; + private GTextField userTF; + private JTextField sourceTF, warnTA; // State vars - protected Credential myCredential; - protected Result eResult; - protected Boolean isReset; + private Credential myCredential; + private Result eResult; + private Boolean isReset; /** - * Constructor + * Standard Constructor */ public CredentialPanel(Component aParent) { @@ -85,14 +93,7 @@ public class CredentialPanel extends GlassPanel implements ActionListener // Reset the dialog isReset = true; - EventQueue.invokeLater(new Runnable() - { - @Override - public void run() - { - updateGui(); - } - }); + EventQueue.invokeLater(() -> updateGui()); } public void setTitle(String aTitle) @@ -101,12 +102,9 @@ public class CredentialPanel extends GlassPanel implements ActionListener } @Override - public void actionPerformed(ActionEvent e) + public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = e.getSource(); - + var source = aEvent.getSource(); if (source == ignoreB) { // Hide the dialog @@ -126,8 +124,7 @@ public class CredentialPanel extends GlassPanel implements ActionListener final Credential aCredential = new Credential(userTF.getText(), passTF.getPassword()); final String uriRoot = sourceTF.getText(); - new Thread() - { + new Thread() { @Override public void run() { @@ -147,8 +144,7 @@ public class CredentialPanel extends GlassPanel implements ActionListener aResult = NetUtil.checkCredentials(uriRoot, aCredential); // Update the Gui - EventQueue.invokeLater(new Runnable() - { + EventQueue.invokeLater(new Runnable() { @Override public void run() { @@ -189,10 +185,6 @@ public class CredentialPanel extends GlassPanel implements ActionListener */ private void buildGuiArea() { - Dimension aDimension; - JLabel tmpL; - String aStr; - // Form the grid bag constraints setLayout(new MigLayout("", "[right][grow][][]")); @@ -201,7 +193,7 @@ public class CredentialPanel extends GlassPanel implements ActionListener add(titleL, "growx,span 4,wrap"); // Source area - tmpL = new JLabel("Source:"); + JLabel tmpL = new JLabel("Source:"); add(tmpL); sourceTF = GuiUtil.createUneditableTextField("http://www.google.edu"); @@ -220,19 +212,17 @@ public class CredentialPanel extends GlassPanel implements ActionListener add(passTF, "growx,span 3,wrap"); // Warn area - aStr = "Please enter the credentials for accessing the data."; - warnTA = GuiUtil.createUneditableTextField(aStr); + String tmpStr = "Please enter the credentials for accessing the data."; + warnTA = GuiUtil.createUneditableTextField(tmpStr); warnTA.setForeground(warnColor); add(warnTA, "growx,span 4,wrap"); // Action area - aDimension = GuiUtil.computePreferredJButtonSize("Ignore", "Accept"); - ignoreB = GuiUtil.createJButton("Ignore", this, aDimension); - acceptB = GuiUtil.createJButton("Accept", this, aDimension); - add(ignoreB, "skip 2,span 1"); - add(acceptB, "span 1"); - - setBorder(new BevelBorder(BevelBorder.RAISED)); + Dimension tmpDim = GuiUtil.computePreferredJButtonSize("Ignore", "Accept"); + ignoreB = GuiUtil.createJButton("Ignore", this, tmpDim); + acceptB = GuiUtil.createJButton("Accept", this, tmpDim); + add(ignoreB, "skip 2"); + add(acceptB, ""); } /** @@ -241,7 +231,6 @@ public class CredentialPanel extends GlassPanel implements ActionListener private void updateGui() { boolean isEnabled; - if (isReset == null) { warnTA.setText("Checking the credentials..."); @@ -262,20 +251,20 @@ public class CredentialPanel extends GlassPanel implements ActionListener switch (eResult) { case BadCredentials: - warnTA.setText("Credentials are invalid."); - break; + warnTA.setText("Credentials are invalid."); + break; case ConnectFailure: - warnTA.setText("Failed to connect to resource."); - break; + warnTA.setText("Failed to connect to resource."); + break; case UnreachableHost: - warnTA.setText("Unreachable host."); - break; + warnTA.setText("Unreachable host."); + break; default: - warnTA.setText("Unreconzied error. Error: " + eResult); - break; + warnTA.setText("Unreconzied error. Error: " + eResult); + break; } } } diff --git a/src/glum/gui/panel/CustomFocusTraversalPolicy.java b/src/glum/gui/panel/CustomFocusTraversalPolicy.java index a0b8d9f..5d5500d 100644 --- a/src/glum/gui/panel/CustomFocusTraversalPolicy.java +++ b/src/glum/gui/panel/CustomFocusTraversalPolicy.java @@ -1,91 +1,103 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import java.awt.Component; -import java.awt.Container; -import java.awt.FocusTraversalPolicy; +import java.awt.*; import java.util.ArrayList; -import com.google.common.collect.Lists; - +/** + * Implementation of {@link FocusTraversalPolicy}. + *

    + * Components will be traversed based on the order they have been added to this object. + * + * @author lopeznr1 + */ public class CustomFocusTraversalPolicy extends FocusTraversalPolicy { - protected ArrayList itemList; - + // State vars + private ArrayList itemL; + + /** Standard Constructor */ public CustomFocusTraversalPolicy() { - itemList = Lists.newArrayList(); + itemL = new ArrayList<>(); } - + /** * Method to add an item to the end of the FocusTraversalPolicy */ public void addComponent(Component aItem) { - itemList.add(aItem); + itemL.add(aItem); } @Override public Component getComponentAfter(Container focusCycleRoot, Component aComponent) { - Component aComp; - int cIndex, tIndex; - // Bail if the component is not in our list - cIndex = itemList.indexOf(aComponent); + var cIndex = itemL.indexOf(aComponent); if (cIndex < 0) return getFirstComponent(focusCycleRoot); - - tIndex = cIndex; + + var tIndex = cIndex; while (true) { // Iterate through the circular loop tIndex++; - if (tIndex == itemList.size()) + if (tIndex == itemL.size()) tIndex = 0; - + // Ensure the item is focusable - aComp = itemList.get(tIndex); - if (checkFocusability(aComp) == true) - return aComp; - + var tmpComp = itemL.get(tIndex); + if (checkFocusability(tmpComp) == true) + return tmpComp; + // Bail if we have made an full loop if (tIndex == cIndex) break; } - - return itemList.get(cIndex); + + return itemL.get(cIndex); } @Override public Component getComponentBefore(Container focusCycleRoot, Component aComponent) { - Component aComp; - int cIndex, tIndex; - // Bail if the component is not in our list - cIndex = itemList.indexOf(aComponent); + var cIndex = itemL.indexOf(aComponent); if (cIndex < 0) return getLastComponent(focusCycleRoot); - - tIndex = cIndex; + + var tIndex = cIndex; while (true) { // Iterate through the circular loop tIndex--; if (tIndex == -1) - tIndex = itemList.size() - 1; - + tIndex = itemL.size() - 1; + // Ensure the item is focusable - aComp = itemList.get(tIndex); - if (checkFocusability(aComp) == true) - return aComp; - + var tmpComp = itemL.get(tIndex); + if (checkFocusability(tmpComp) == true) + return tmpComp; + // Bail if we have made an full loop if (tIndex == cIndex) break; } - - return itemList.get(cIndex); + + return itemL.get(cIndex); } @Override @@ -97,58 +109,49 @@ public class CustomFocusTraversalPolicy extends FocusTraversalPolicy @Override public Component getFirstComponent(Container focusCycleRoot) { - Component aComp; - // Bail if no components to traverse - if (itemList.isEmpty() == true) + if (itemL.isEmpty() == true) return null; - - aComp = itemList.get(0); - if (checkFocusability(aComp) == true) - return aComp; - - return getComponentAfter(focusCycleRoot, aComp); + + var tmpComp = itemL.get(0); + if (checkFocusability(tmpComp) == true) + return tmpComp; + + return getComponentAfter(focusCycleRoot, tmpComp); } @Override public Component getLastComponent(Container focusCycleRoot) { - Component aComp; - // Bail if no components to traverse - if (itemList.isEmpty() == true) + if (itemL.isEmpty() == true) return null; - - aComp = itemList.get(itemList.size()-1); - if (checkFocusability(aComp) == true) - return aComp; - - return getComponentBefore(focusCycleRoot, aComp); + + var tmpComp = itemL.get(itemL.size() - 1); + if (checkFocusability(tmpComp) == true) + return tmpComp; + + return getComponentBefore(focusCycleRoot, tmpComp); } - /** - * Utility method to test to see if a Component can grab the focus. - * To grab the focus a component must be: - * - focusable - * - enabled - * - visible - * - be in a container that is being shown. - */ + * Utility method to test to see if a Component can grab the focus. To grab the focus a component must be: - + * focusable - enabled - visible - be in a container that is being shown. + */ protected boolean checkFocusability(Component aComp) { if (aComp.isFocusable() == false) return false; - + if (aComp.isEnabled() == false) return false; - + if (aComp.isVisible() == false) return false; - + if (aComp.isShowing() == false) return false; - + return true; } diff --git a/src/glum/gui/panel/FontInputPanel.java b/src/glum/gui/panel/FontInputPanel.java new file mode 100644 index 0000000..b835cc1 --- /dev/null +++ b/src/glum/gui/panel/FontInputPanel.java @@ -0,0 +1,209 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.panel; + +import java.awt.Font; +import java.awt.GraphicsEnvironment; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import com.google.common.collect.Range; + +import glum.gui.component.GComboBox; +import glum.gui.component.GNumberField; +import glum.unit.*; +import net.miginfocom.swing.MigLayout; + +/** + * User input component that allows the user to specify a {@link Font}. + * + * @author lopeznr1 + */ +public class FontInputPanel extends GPanel implements ActionListener, ChangeListener +{ + // Gui components + private GComboBox nameBox; + private JLabel sizeL; + private JSlider sizeS; + private GNumberField sizeNF; + private JCheckBox boldCB, italicCB; + private JTextField previewTF; + + /** + * Standard Constructor + */ + public FontInputPanel() + { + // Build the gui area + buildGuiArea(); + + // Set in the default Font + var defFont = UIManager.getFont("Label.font"); + setFontConfig(defFont); + } + + /** + * Returns the selected Font. + */ + public Font getFontConfig() + { + var name = nameBox.getChosenItem(); + + var size = sizeS.getValue(); + + var style = Font.PLAIN; + if (boldCB.isSelected() == true) + style |= Font.BOLD; + if (italicCB.isSelected() == true) + style |= Font.ITALIC; + + return new Font(name, style, size); + } + + /** + * Sets in the current selected Font. + */ + public void setFontConfig(Font aFont) + { + synchronizeGui(aFont); + } + + @Override + public void actionPerformed(ActionEvent aEvent) + { + // Perform GUI updates + var source = aEvent.getSource(); + updateGui(source); + + // Notify the listeners + notifyListeners(source, ID_UPDATE); + } + + @Override + public void setEnabled(boolean aBool) + { + nameBox.setEnabled(aBool); + boldCB.setEnabled(aBool); + italicCB.setEnabled(aBool); + sizeL.setEnabled(aBool); + sizeS.setEnabled(aBool); + sizeNF.setEnabled(aBool); + + previewTF.setEnabled(aBool); + } + + @Override + public void stateChanged(ChangeEvent aEvent) + { + // Perform GUI updates + var source = aEvent.getSource(); + updateGui(source); + + if (source == sizeS) + { + // Notify the listeners + if (sizeS.getValueIsAdjusting() == false) + notifyListeners(source, ID_UPDATE); + else + notifyListeners(source, ID_UPDATING); + } + } + + /** + * Forms the actual gui + */ + private void buildGuiArea() + { + setLayout(new MigLayout("", "0[right][][fill][grow]0", "0[]0")); + var nameArr = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames(); + + // Name area + var tmpL = new JLabel("Name:"); + nameBox = new GComboBox<>(this, nameArr); + add("", tmpL); + add("span 2", nameBox); + + // Preview area + previewTF = new JTextField("Sample Text"); + previewTF.setHorizontalAlignment(JTextField.CENTER); + add("growx,growy,pushy,spany,wrap", previewTF); + + // Style area + tmpL = new JLabel("Style:"); + boldCB = new JCheckBox("Bold", false); + boldCB.addActionListener(this); + italicCB = new JCheckBox("Italic", false); + italicCB.addActionListener(this); + add("", tmpL); + add("", boldCB); + add("wrap", italicCB); + + // Size area + Range sizeRange = Range.closed(4.0, 72.0); + UnitProvider countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, 0)); + sizeL = new JLabel("Size:", JLabel.CENTER); + sizeNF = new GNumberField(this, countUP, sizeRange); + sizeS = new JSlider(JSlider.HORIZONTAL, 4, 72, 12); + add("", sizeL); + add("span 2,split,w 24::", sizeNF); + add("growx", sizeS); + } + + /** + * Syncs the GUI to match aFont. + */ + private void synchronizeGui(Font aFont) + { + nameBox.setChosenItem(aFont.getName()); + boldCB.setSelected(aFont.isBold()); + italicCB.setSelected(aFont.isItalic()); + + // Update the components related to size + int size = aFont.getSize(); + if (size != sizeNF.getValue()) + sizeNF.setValue(size); + + sizeS.removeChangeListener(this); + sizeS.setValue(size); + sizeS.addChangeListener(this); + } + + /** + * Updates the gui to reflect the source that has changed + */ + private void updateGui(Object aSource) + { + if (aSource == sizeS) + { + int size = sizeS.getValue(); + sizeNF.setValue(size); + } + else if (aSource == sizeNF) + { + int size = sizeNF.getValueAsInt(4); + sizeS.removeChangeListener(this); + sizeS.setValue(size); + sizeS.addChangeListener(this); + } + + // Update the preview area + var currFont = getFontConfig(); + previewTF.setFont(currFont); + } + +} diff --git a/src/glum/gui/panel/GPanel.java b/src/glum/gui/panel/GPanel.java index bccb60f..6e7bbb3 100644 --- a/src/glum/gui/panel/GPanel.java +++ b/src/glum/gui/panel/GPanel.java @@ -1,30 +1,46 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.gui.panel.generic.GenericCodes; - import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.*; +import java.util.LinkedHashSet; +import java.util.Set; import javax.swing.JPanel; +import glum.gui.panel.generic.GenericCodes; + /** - * JPanel that supports registration of ActionListeners. Derived classes will be responsible for determining when an ActionEvent should be fired. + * JPanel that supports registration of {@link ActionListener}s. Derived classes will be responsible for determining + * when an {@link ActionEvent} should be fired. + * + * @author lopeznr1 */ public class GPanel extends JPanel implements GenericCodes { // State vars - protected Set myListeners; + protected Set myListenerS; + /** Standard Constructor */ public GPanel() { - super(); - - myListeners = new LinkedHashSet<>(); + myListenerS = new LinkedHashSet<>(); } /** - * Add an ActionListener to this GPanel + * Add an {@link ActionListener} from this panel. */ public synchronized void addActionListener(ActionListener aListener) { @@ -32,40 +48,49 @@ public class GPanel extends JPanel implements GenericCodes if (aListener == null) throw new RuntimeException("Listener should not be null."); - myListeners.add(aListener); + myListenerS.add(aListener); } /** - * Remove an ActionListener to this GPanel + * Remove an {@link ActionListener} from this panel. */ - public synchronized void removeActionListener(ActionListener aListener) + public synchronized void delActionListener(ActionListener aListener) { - myListeners.remove(aListener); + myListenerS.remove(aListener); } /** - * Send out notification to all of the ActionListeners + * Send out notification to all of the {@link ActionListener}s. */ public void notifyListeners(Object aSource, int aId, String aCommand) { - Set tmpListeners; - // Get a copy of the current set of listeners + Set tmpListenerS; synchronized (this) { - tmpListeners = new LinkedHashSet<>(myListeners); + tmpListenerS = new LinkedHashSet<>(myListenerS); } // Notify our listeners - for (ActionListener aListener : tmpListeners) + for (ActionListener aListener : tmpListenerS) aListener.actionPerformed(new ActionEvent(aSource, aId, aCommand)); } /** - * Send out notification to all of the ActionListeners + * Send out notification to all of the {@link ActionListener}s. */ public void notifyListeners(Object aSource, int aId) { + // Delegate notifyListeners(aSource, aId, ""); } + + /** + * Send out notification to all of the {@link ActionListener}s. + */ + public void notifyListeners(Object aSource) + { + // Delegate + notifyListeners(aSource, 0, ""); + } } diff --git a/src/glum/gui/panel/GlassPane.java b/src/glum/gui/panel/GlassPane.java index b725353..9f393c1 100644 --- a/src/glum/gui/panel/GlassPane.java +++ b/src/glum/gui/panel/GlassPane.java @@ -1,36 +1,50 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.gui.GuiUtil; -import glum.gui.panel.CustomFocusTraversalPolicy; -import glum.zio.*; - import java.awt.*; -import java.awt.event.ComponentEvent; -import java.awt.event.ComponentListener; -import java.awt.event.MouseEvent; +import java.awt.event.*; import java.io.IOException; -import java.util.Set; +import java.util.LinkedHashSet; import javax.swing.*; import javax.swing.event.MouseInputAdapter; +import glum.gui.GuiUtil; +import glum.zio.*; import net.miginfocom.swing.MigLayout; -import com.google.common.collect.Sets; - +/** + * GUI element useful for holding the provided child UI components. This GUI component will be displayed as a floating + * "modal" glass pane over it's parent component. + * + * @author lopeznr1 + */ public class GlassPane extends JComponent implements ZioObj, ComponentListener { // Communicator vars - protected Component parentComp; - protected WaftPanel childComp; + private final Component parentComp; + protected final WaftPanel childComp; + + private RootPaneContainer rootPane; - protected RootPaneContainer rootPane; - // State vars - protected Color fillColor; - protected CustomFocusTraversalPolicy myFocusPolicy; - protected MigLayout refLayout; + private Color fillColor; + private CustomFocusTraversalPolicy myFocusPolicy; + private MigLayout workLayout; + /** Standard Constructor */ public GlassPane(Component aRefParent, JComponent aDisplayPane) { // Communicator vars @@ -38,26 +52,26 @@ public class GlassPane extends JComponent implements ZioObj, ComponentListener childComp = new WaftPanel(aDisplayPane, this); // Build the GUI - refLayout = new MigLayout(); - setLayout(refLayout); + workLayout = new MigLayout(); + setLayout(workLayout); // add("pos 150px 100px", childComp); add("", childComp); - this.validate(); - this.revalidate(); - + validate(); + revalidate(); + // Register for events of interest - childComp.addComponentListener(this); + childComp.addComponentListener(this); // Set up the GlassPanelListener handler GlassPaneListener aListener = new GlassPaneListener(this, childComp); addMouseListener(aListener); addMouseMotionListener(aListener); - + rootPane = null; fillColor = new Color(96, 96, 96, 96); } - + /** * Set in an alternative shade color. */ @@ -65,10 +79,10 @@ public class GlassPane extends JComponent implements ZioObj, ComponentListener { fillColor = aColor; } - + /** - * Method to change the end users ability to resize a panel. This method call is just a suggestion - * and the GUI toolkit may totally ignore this call. + * Method to change the end users ability to resize a panel. This method call is just a suggestion and the GUI + * toolkit may totally ignore this call. */ public void setResizable(boolean aBool) { @@ -82,145 +96,78 @@ public class GlassPane extends JComponent implements ZioObj, ComponentListener } @Override - public void componentMoved(ComponentEvent e) + public void componentMoved(ComponentEvent aEvent) { - if (e.getSource() == childComp) - refLayout.setComponentConstraints(childComp, "pos " + childComp.getLocation().x + " " + childComp.getLocation().y); + if (aEvent.getSource() == childComp) + workLayout.setComponentConstraints(childComp, + "pos " + childComp.getLocation().x + " " + childComp.getLocation().y); } @Override - public void componentShown(ComponentEvent e) + public void componentShown(ComponentEvent aEvent) { ; // Nothing to do } @Override - public void componentHidden(ComponentEvent e) + public void componentHidden(ComponentEvent aEvent) { ; // Nothing to do } @Override - public void setVisible(boolean isVisible) + public void setVisible(boolean aIsVisible) { - JLayeredPane layeredPane; - KeyboardFocusManager aManager; - // Initialize the GUI if it has not been initialed // Once initialized rootPane != null if (rootPane == null) initializeGui(); - - super.setVisible(isVisible); - + + super.setVisible(aIsVisible); + // Activate/Deactive managing the focus - setFocusCycleRoot(isVisible); - setFocusTraversalPolicyProvider(isVisible); - + setFocusCycleRoot(aIsVisible); + setFocusTraversalPolicyProvider(aIsVisible); + // Bail if this GlassPane is no longer visible - if (isVisible == false) + if (aIsVisible == false) return; // Ensure this GlassPane is at the top - layeredPane = rootPane.getLayeredPane(); + var layeredPane = rootPane.getLayeredPane(); layeredPane.moveToFront(this); // Clear out the old focus (from the underlay component) - aManager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); - aManager.clearGlobalFocusOwner(); - + var tmpKFM = KeyboardFocusManager.getCurrentKeyboardFocusManager(); + tmpKFM.clearGlobalFocusOwner(); + // Wait and ensure that the GlassPane is visible, before we request the focus - SwingUtilities.invokeLater(new Runnable() - { + SwingUtilities.invokeLater(new Runnable() { @Override public void run() { - Component aComponent; - - aComponent = myFocusPolicy.getDefaultComponent(null); - if (aComponent != null) - aComponent.requestFocusInWindow(); - + var tmpComponent = myFocusPolicy.getDefaultComponent(null); + if (tmpComponent != null) + tmpComponent.requestFocusInWindow(); + repaint(); } }); - } - + } + @Override public void zioRead(ZinStream aStream) throws IOException { + aStream.readVersion(0); childComp.zioRead(aStream); } - + @Override public void zioWrite(ZoutStream aStream) throws IOException { + aStream.writeVersion(0); childComp.zioWrite(aStream); } - - /** - * Sets up the GUI for the GlassPane - */ - protected void initializeGui() - { - Set compSet; - JLayeredPane layeredPane; - ComponentTracker aTracker; - Dimension prefDim; - Point prefLoc; - - // Ensure this method is not called twice - if (rootPane != null) - throw new RuntimeException("GlassPane.initializeGui() has been already called."); - - // Retrieve the associated rootPane and layeredPane - rootPane = GuiUtil.getRootPaneContainer(parentComp); - - // Add the GlassPane into the layeredPane - layeredPane = rootPane.getLayeredPane(); - layeredPane.setLayer(this, JLayeredPane.PALETTE_LAYER); - layeredPane.add(this); - setSize(layeredPane.getSize()); -// layeredPane.validate(); - layeredPane.revalidate(); - - // Determine initial location and size of the child component - prefDim = childComp.getPrefDim(); - if (prefDim == null) - prefDim = childComp.getPreferredSize(); - - prefLoc = childComp.getPrefLoc(); - if (prefLoc == null) - prefLoc = new Point(getWidth()/2 - prefDim.width/2, getHeight()/2 - prefDim.height/2); - -// System.out.println("prefLoc: " + prefLoc + " prefDim: " + prefDim); - childComp.setLocation(prefLoc); - childComp.setSize(prefDim); -//childComp.revalidate(); -//layeredPane.revalidate(); -//this.validate(); -//this.revalidate(); - - // Setup the focus policy to consist of any child components of type - // AbstractButton, JComboBox, JTextField, JTable - compSet = Sets.newLinkedHashSet(); - Class[] classArr = {AbstractButton.class, JComboBox.class, JTextField.class, JTable.class}; - GuiUtil.locateAllSubComponents(childComp, compSet, classArr); - - myFocusPolicy = new CustomFocusTraversalPolicy(); - for (Component aItem : compSet) - myFocusPolicy.addComponent(aItem); - - setFocusCycleRoot(true); - setFocusTraversalPolicyProvider(true); - setFocusTraversalPolicy(myFocusPolicy); - - // Set up a ComponentTracker to keep this GlassPane linked to the - // appropriate Components of interest - aTracker = new ComponentTracker(this); - aTracker.setHiddenTracker((Component)rootPane); - aTracker.setResizedTracker(layeredPane); - } @Override protected void paintComponent(Graphics g) @@ -242,12 +189,71 @@ public class GlassPane extends JComponent implements ZioObj, ComponentListener System.out.println("Undefined object being " + aStr + "..."); } + /** + * Sets up the GUI for the GlassPane + */ + private void initializeGui() + { + // Ensure this method is not called twice + if (rootPane != null) + throw new RuntimeException("GlassPane.initializeGui() has been already called."); + + // Retrieve the associated rootPane and layeredPane + rootPane = GuiUtil.getRootPaneContainer(parentComp); + + // Add the GlassPane into the layeredPane + var layeredPane = rootPane.getLayeredPane(); + layeredPane.setLayer(this, JLayeredPane.PALETTE_LAYER); + layeredPane.add(this); + setSize(layeredPane.getSize()); +// layeredPane.validate(); + layeredPane.revalidate(); + + // Determine initial location and size of the child component + var prefDim = childComp.getPrefDim(); + if (prefDim == null) + prefDim = childComp.getPreferredSize(); + + var prefLoc = childComp.getPrefLoc(); + if (prefLoc == null) + prefLoc = new Point(getWidth() / 2 - prefDim.width / 2, getHeight() / 2 - prefDim.height / 2); + +// System.out.println("prefLoc: " + prefLoc + " prefDim: " + prefDim); + childComp.setLocation(prefLoc); + childComp.setSize(prefDim); +//childComp.revalidate(); +//layeredPane.revalidate(); +//this.validate(); +//this.revalidate(); + + // Setup the focus policy to consist of any child components of type + // AbstractButton, JComboBox, JTextField, JTable + var compS = new LinkedHashSet(); + Class[] classArr = { AbstractButton.class, JComboBox.class, JTextField.class, JTable.class }; + GuiUtil.locateAllSubComponents(childComp, compS, classArr); + + myFocusPolicy = new CustomFocusTraversalPolicy(); + for (Component aItem : compS) + myFocusPolicy.addComponent(aItem); + + setFocusCycleRoot(true); + setFocusTraversalPolicyProvider(true); + setFocusTraversalPolicy(myFocusPolicy); + + // Set up a ComponentTracker to keep this GlassPane linked to the + // appropriate Components of interest + var tmpTracker = new ComponentTracker(this); + tmpTracker.setHiddenTracker((Component) rootPane); + tmpTracker.setResizedTracker(layeredPane); + } + } - /** - * Utility class to capture all mouse interactions and redispatch the events only - * to the subcomponents associated with the container. + * Helper class to capture all mouse interactions and redispatch the events only to the subcomponents associated with + * the container. + * + * @author lopeznr1 */ class GlassPaneListener extends MouseInputAdapter { @@ -302,44 +308,39 @@ class GlassPaneListener extends MouseInputAdapter redispatchMouseEvent(e, true); } - //A more finished version of this method would - //handle mouse-dragged events specially. - private void redispatchMouseEvent(MouseEvent e, boolean repaint) + // A more finished version of this method would + // handle mouse-dragged events specially. + private void redispatchMouseEvent(MouseEvent aEvent, boolean aIsRepaint) { + var glassPanePoint = aEvent.getPoint(); + var container = contentPane; + var containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane); - Point glassPanePoint = e.getPoint(); - Container container = contentPane; - Point containerPoint = SwingUtilities.convertPoint(glassPane, glassPanePoint, contentPane); - - //The mouse event is probably over the content pane. - //Find out exactly which component it's over. - Component component; - - component = SwingUtilities.getDeepestComponentAt(container, containerPoint.x, containerPoint.y); + // The mouse event is probably over the content pane. + // Find out exactly which component it's over. + var component = SwingUtilities.getDeepestComponentAt(container, containerPoint.x, containerPoint.y); if (component != null) { - //Forward events over the to the component - dispatchEvent(component, e); + // Forward events over the to the component + dispatchEvent(component, aEvent); } // Update the glass pane if requested. - if (repaint) + if (aIsRepaint) glassPane.repaint(); } private void dispatchEvent(Component aComponent, MouseEvent aEvent) { - Point aPt; - if (aComponent == null || aEvent == null) return; - aPt = SwingUtilities.convertPoint(glassPane, aEvent.getPoint(), aComponent); - if (aPt == null) + var tmpPt = SwingUtilities.convertPoint(glassPane, aEvent.getPoint(), aComponent); + if (tmpPt == null) return; - aComponent.dispatchEvent(new MouseEvent(aComponent, aEvent.getID(), aEvent.getWhen(), aEvent.getModifiers(), - aPt.x, aPt.y, aEvent.getClickCount(), aEvent.isPopupTrigger())); + aComponent.dispatchEvent(new MouseEvent(aComponent, aEvent.getID(), aEvent.getWhen(), aEvent.getModifiersEx(), + tmpPt.x, tmpPt.y, aEvent.getClickCount(), aEvent.isPopupTrigger())); } } diff --git a/src/glum/gui/panel/GlassPaneAncient.java b/src/glum/gui/panel/GlassPaneAncient.java index abe2e68..31f480a 100644 --- a/src/glum/gui/panel/GlassPaneAncient.java +++ b/src/glum/gui/panel/GlassPaneAncient.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; import glum.gui.GuiUtil; diff --git a/src/glum/gui/panel/GlassPanel.java b/src/glum/gui/panel/GlassPanel.java index f0248aa..faa35aa 100644 --- a/src/glum/gui/panel/GlassPanel.java +++ b/src/glum/gui/panel/GlassPanel.java @@ -1,34 +1,56 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.gui.GuiUtil; -import glum.zio.*; - import java.awt.Component; import java.awt.Dimension; import java.io.IOException; +import javax.swing.border.BevelBorder; + +import glum.gui.GuiUtil; +import glum.zio.*; + +/** + * Panel that is typically used to display a model panel that pops up over a top level component. + * + * @author lopeznr1 + */ public abstract class GlassPanel extends GPanel implements ZioObj { // State vars - protected Component myGlassPane; + private Component myGlassPane; + /** Standard Constructor */ public GlassPanel(Component aRefParent) { this(aRefParent, PaneType.GlassPanel); } + /** Complex Constructor */ public GlassPanel(Component aRefParent, PaneType aType) { - super(); - if (aType == PaneType.GlassPanel) myGlassPane = new GlassPane(aRefParent, this); else if (aType == PaneType.GlassPanelAncient) myGlassPane = new GlassPaneAncient(aRefParent, this); else myGlassPane = new StandardPane(aRefParent, this); + + setBorder(new BevelBorder(BevelBorder.RAISED)); } - + /** * Call this method to change the end-user's ability to resize this component. Note this call is just a suggestion * and may be totally ignored by the GUI toolkit. @@ -46,13 +68,13 @@ public abstract class GlassPanel extends GPanel implements ZioObj public void setVisibleAsModal() { setVisible(true); - + if (myGlassPane instanceof GlassPane) GuiUtil.modalWhileVisible(myGlassPane); - else + else GuiUtil.modalWhileVisible(getParent()); } - + @Override public void setSize(Dimension aDim) { @@ -61,13 +83,13 @@ public abstract class GlassPanel extends GPanel implements ZioObj else super.setSize(aDim); } - + @Override public void setSize(int width, int height) { setSize(new Dimension(width, height)); } - + @Override public void setVisible(boolean isVisible) { @@ -87,23 +109,23 @@ public abstract class GlassPanel extends GPanel implements ZioObj if (myGlassPane.isVisible() == true) myGlassPane.repaint(); } - + @Override public void zioRead(ZinStream aStream) throws IOException { aStream.readVersion(0); - + if (myGlassPane instanceof ZioObj) ((ZioObj)myGlassPane).zioRead(aStream); } - + @Override public void zioWrite(ZoutStream aStream) throws IOException { aStream.writeVersion(0); - + if (myGlassPane instanceof ZioObj) ((ZioObj)myGlassPane).zioWrite(aStream); } - + } diff --git a/src/glum/gui/panel/NoticePanel.java b/src/glum/gui/panel/NoticePanel.java new file mode 100644 index 0000000..81f54c1 --- /dev/null +++ b/src/glum/gui/panel/NoticePanel.java @@ -0,0 +1,187 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.panel; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Map; + +import javax.swing.*; +import javax.swing.border.LineBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; + +import com.google.common.collect.ImmutableMap; + +import glum.gui.FocusUtil; +import glum.gui.GuiUtil; +import glum.gui.action.ClickAction; +import glum.gui.component.model.GListModel; +import net.miginfocom.swing.MigLayout; + +/** + * {@link GlassPanel} used to provide the user with a listing of notices. + *

    + * The title and list of notices can be customized. + * + * @author lopeznr1 + */ +public class NoticePanel extends GlassPanel implements ActionListener, ListSelectionListener +{ + // GUI vars + private JLabel titleL; + private JList itemJL; + private JTextArea infoTA; + private JButton closeB; + + // State vars + private ImmutableMap notificationM; + + /** Standard Constructor */ + public NoticePanel(Component aParent, String aTitle, int sizeX, int sizeY) + { + super(aParent); + + notificationM = ImmutableMap.of(); + + buildGuiArea(); + setSize(sizeX, sizeY); + setTitle(aTitle); + + // Set up keyboard short cuts + FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(closeB)); + FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB)); + } + + /** Simplified Constructor */ + public NoticePanel(Component aParent, String aTitle) + { + this(aParent, aTitle, 275, 350); + } + + /** Simplified Constructor */ + public NoticePanel(Component aParent) + { + this(aParent, "Untitled", 275, 350); + } + + /** + * Sets the notifications to be displayed. + */ + public void setNotifications(Map aNotificationM) + { + notificationM = ImmutableMap.copyOf(aNotificationM); + + // Update the item JList + itemJL.removeListSelectionListener(this); + var tmpModel = new GListModel<>(aNotificationM.keySet()); + itemJL.setModel(tmpModel); + itemJL.addListSelectionListener(this); + + String tmpItem = null; + if (notificationM.size() > 0) + tmpItem = notificationM.keySet().iterator().next(); + itemJL.setSelectedValue(tmpItem, true); + + updateGui(); + } + + /** + * Sets the font of the info area. + */ + public void setFontInfo(Font aFont) + { + infoTA.setFont(aFont); + } + + /** + * Sets the tab size associated with the info area. + */ + public void setTabSize(int aSize) + { + infoTA.setTabSize(aSize); + } + + /** + * Sets the title of this panel. + */ + public void setTitle(String aTitle) + { + titleL.setText(aTitle); + } + + @Override + public void actionPerformed(ActionEvent aEvent) + { + var source = aEvent.getSource(); + if (source == closeB) + { + setVisible(false); + notifyListeners(this, ID_CANCEL); + } + + updateGui(); + } + + @Override + public void valueChanged(ListSelectionEvent aEvent) + { + updateGui(); + } + + /** + * Forms the actual GUI + */ + private void buildGuiArea() + { + setLayout(new MigLayout("", "[]", "[]")); + + // Title Area + titleL = new JLabel("Title", JLabel.CENTER); + add(titleL, "growx,span,wrap"); + add(GuiUtil.createDivider(), "growx,h 4!,span,wrap"); + + // Notification area + itemJL = new JList<>(); + itemJL.setBorder(new LineBorder(Color.BLACK)); + itemJL.addListSelectionListener(this); + + infoTA = new JTextArea("No status", 3, 0); + infoTA.setEditable(false); +// infoTA.setOpaque(false); + infoTA.setLineWrap(true); + infoTA.setTabSize(3); + infoTA.setWrapStyleWord(true); + + add(new JScrollPane(itemJL), "w pref+3::,growy,span,split"); + add(new JScrollPane(infoTA), "growx,growy,pushx,pushy,span,wrap"); + + // Action area + closeB = GuiUtil.createJButton("Close", this); + add(closeB, "ax right,span,split"); + } + + /** + * Helper method that keeps various UI components synchronized. + */ + private void updateGui() + { + var tmpKey = itemJL.getSelectedValue(); + var tmpVal = notificationM.get(tmpKey); + + infoTA.setText(tmpVal); + infoTA.setCaretPosition(0); + } +} diff --git a/src/glum/gui/panel/PaneType.java b/src/glum/gui/panel/PaneType.java index 8372b67..9253cc1 100644 --- a/src/glum/gui/panel/PaneType.java +++ b/src/glum/gui/panel/PaneType.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; public enum PaneType diff --git a/src/glum/gui/panel/ShadePane.java b/src/glum/gui/panel/ShadePane.java index b4054ea..52ea35d 100644 --- a/src/glum/gui/panel/ShadePane.java +++ b/src/glum/gui/panel/ShadePane.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; import glum.gui.GuiUtil; diff --git a/src/glum/gui/panel/StandardPane.java b/src/glum/gui/panel/StandardPane.java index 8fed0d9..fac36af 100644 --- a/src/glum/gui/panel/StandardPane.java +++ b/src/glum/gui/panel/StandardPane.java @@ -1,30 +1,44 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import java.awt.Color; -import java.awt.Component; -import java.awt.Graphics; +import java.awt.*; import javax.swing.JComponent; import javax.swing.RootPaneContainer; + import glum.gui.GuiUtil; -import glum.gui.panel.CustomFocusTraversalPolicy; import net.miginfocom.swing.MigLayout; +/** + * UI panel for displaying an overlayed {@link Component}. + * + * @author lopeznr1 + */ public class StandardPane extends JComponent { // Communicator vars - protected Component refParent; - protected JComponent displayPane; + private final Component refParent; + private final JComponent displayPane; - protected RootPaneContainer rootPane; + private RootPaneContainer rootPane; // State vars - protected Color fillColor; - protected CustomFocusTraversalPolicy myFocusPolicy; + private Color fillColor; +// private CustomFocusTraversalPolicy myFocusPolicy; - /** - * Constructor - */ + /** Standard Constructor */ public StandardPane(Component aRefParent, JComponent aDisplayPane) { // Communicator vars @@ -77,7 +91,7 @@ public class StandardPane extends JComponent // Set up a ComponentTracker to keep this ShadePane linked to the rootPane aTracker = new ComponentTracker(this); - aTracker.setResizedTracker((Component)rootPane); + aTracker.setResizedTracker((Component) rootPane); } @Override diff --git a/src/glum/gui/panel/WaftPanel.java b/src/glum/gui/panel/WaftPanel.java index a313a2c..cb4b700 100644 --- a/src/glum/gui/panel/WaftPanel.java +++ b/src/glum/gui/panel/WaftPanel.java @@ -1,29 +1,41 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel; -import glum.gui.panel.nub.HorizontalNub; -import glum.util.MathUtil; -import glum.zio.*; -import glum.zio.util.ZioUtil; - -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Point; +import java.awt.*; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.io.IOException; import javax.swing.JComponent; +import glum.gui.panel.nub.HorizontalNub; +import glum.util.MathUtil; +import glum.zio.*; +import glum.zio.util.ZioUtil; import net.miginfocom.swing.MigLayout; /** - * Panel which provides decorative controls to allow a panel to be moved or resized. This is especially useful for - * wrapping a component that will be thrown into a JLayered pane of sorts. Note it is assumed that WaftPanel is - * contained in a LayoutManager of type MigLayout. - *

    + * Panel which provides decorative controls to allow a panel to be moved or resized. This is useful for wrapping a + * component that will be thrown into a JLayered pane of sorts. Note it is assumed that WaftPanel is contained in a + * LayoutManager of type MigLayout. + *

    * Perhaps, this class should be rewritten so that it draws the move, resize borders, and manually listens to them * rather than delegating to the Nub classes. The current implementation is slightly inefficient, but probably more * flexible as it allows other custom Nubs to be swapped in. + * + * @author lopeznr1 */ public class WaftPanel extends JComponent implements ZioObj, ComponentListener { @@ -31,52 +43,51 @@ public class WaftPanel extends JComponent implements ZioObj, ComponentListener protected Component childComp; protected JComponent parentComp; protected HorizontalNub nMoveComp, sMoveComp; - + // State vars protected Dimension prefDim; protected Point prefLoc; protected double aspectRatio; - - public WaftPanel(Component aChildComp, JComponent aParentComp, boolean topNubOnly) - { - super(); + /** Standard Constructor */ + public WaftPanel(Component aChildComp, JComponent aParentComp, boolean aTopNubOnly) + { childComp = aChildComp; parentComp = aParentComp; - + prefDim = null; prefLoc = null; // Embed the childComp into this WaftPanel and surround with the various nub handles setLayout(new MigLayout("", "0[grow,fill]0[]0", "0[]0[grow,fill]0[]0")); - + nMoveComp = new HorizontalNub(parentComp, this, true); sMoveComp = new HorizontalNub(parentComp, this, false); - add("wrap", nMoveComp); - add("growx,growy,wrap", aChildComp); - if (topNubOnly == false) - add("wrap", sMoveComp); - + add(nMoveComp, "wrap"); + add(aChildComp, "growx,growy,wrap"); + if (aTopNubOnly == false) + add(sMoveComp, "wrap"); + // Register to listen to components events associated with the parent parentComp.addComponentListener(this); } - + + /** Simplified Constructor */ public WaftPanel(Component aChildComp, JComponent aParentComp) { this(aChildComp, aParentComp, false); } - - + /** - * Change whether the user can move the WaftPanel. Note this will automatically - * disabling the resizing of the WaftPanel. + * Change whether the user can move the WaftPanel. Note this will automatically disabling the resizing of the + * WaftPanel. */ public void setMovable(boolean aBool) { nMoveComp.setVisible(false); sMoveComp.setVisible(false); } - + /** * Changes whether the associated nubs will provide resize controls. */ @@ -85,7 +96,7 @@ public class WaftPanel extends JComponent implements ZioObj, ComponentListener nMoveComp.setResizable(aBool); sMoveComp.setResizable(aBool); } - + /** * Returns the current targeted and locked dimension */ @@ -104,66 +115,61 @@ public class WaftPanel extends JComponent implements ZioObj, ComponentListener { if (prefLoc == null) return null; - + return new Point(prefLoc); } - + @Override public void setLocation(Point aLoc) { // Update the preferred location prefLoc = new Point(aLoc); - + setLocationAndLock(aLoc); } - + @Override public void setLocation(int x, int y) { setLocation(new Point(x, y)); } - + @Override public void setSize(Dimension aDim) { // Update the preferred location prefDim = new Dimension(aDim); - + super.setSize(aDim); } - + @Override public void componentResized(ComponentEvent aEvent) { - Point targLoc; - Dimension targDim; - Dimension minDim; - - Dimension parentDim; - // Respond only to resizes of our parent if (aEvent.getComponent() != parentComp) return; - + // Record the current WaftPanels as preferences if they have not been recorded if (prefDim == null) prefDim = getSize(); - + if (prefLoc == null) prefLoc = getLocation(); - + // Retrieve the targDim, targLoc, and the minDim - minDim = getMinimumSize(); - targDim = new Dimension(prefDim); - targLoc = new Point(prefLoc); - + var minDim = getMinimumSize(); + var targDim = new Dimension(prefDim); + var targLoc = new Point(prefLoc); + // Ensure the targDim is no larger than parentDim - parentDim = parentComp.getSize(); + var parentDim = parentComp.getSize(); targDim.width = MathUtil.boundRange(0, parentDim.width, targDim.width); targDim.height = MathUtil.boundRange(0, parentDim.height, targDim.height); - + // Attempt to force ourself into the constraint of the parentComp - MathUtil.forceConstraints(targLoc, targDim, 0, 0, parentComp.getWidth(), parentComp.getHeight(), new Dimension(0, 0), true); + MathUtil.forceConstraints(targLoc, targDim, 0, 0, parentComp.getWidth(), parentComp.getHeight(), + new Dimension(0, 0), true); // Ensure the minimum constraints are not violated targDim.width = MathUtil.boundRange(minDim.width, targDim.width, targDim.width); @@ -173,7 +179,7 @@ public class WaftPanel extends JComponent implements ZioObj, ComponentListener setLocationAndLock(targLoc); setPreferredSize(targDim); super.setSize(targDim); - + parentComp.revalidate(); } @@ -194,46 +200,50 @@ public class WaftPanel extends JComponent implements ZioObj, ComponentListener { ; // Nothing to do } - + @Override public void zioRead(ZinStream aStream) throws IOException { aStream.readVersion(0); - + prefDim = ZioUtil.readDimension(aStream); prefLoc = ZioUtil.readPoint(aStream); + + if (prefLoc != null && prefDim != null) + { + setLocation(prefLoc); + setPreferredSize(prefDim); + } } @Override public void zioWrite(ZoutStream aStream) throws IOException { aStream.writeVersion(0); - + ZioUtil.writeDimension(aStream, prefDim); ZioUtil.writePoint(aStream, prefLoc); } /** * Helper method to set the location of this WaftPanel relative to its parent. - *

    + *

    * Adjustments are made to our constraints associated with the layout manager. This is needed so that when the layout * manager is (re)validated any movement or resize changes made will stick. Note we assume that we are placed in a * MigLayout manager. */ private void setLocationAndLock(Point aLoc) { - MigLayout rootLayout; - // Adjust our constraints associated in the layout manager. This is needed so that when the layout manager is // (re)validated any movement or resize changes made will stick. // Note we assume that we are placed in a MigLayout manager. - rootLayout = (MigLayout)parentComp.getLayout(); + var rootLayout = (MigLayout) parentComp.getLayout(); if (rootLayout == null) { super.setLocation(aLoc.x, aLoc.y); return; } - + rootLayout.setComponentConstraints(this, "pos " + aLoc.x + "px " + aLoc.y + "px"); rootLayout.invalidateLayout(null); } diff --git a/src/glum/gui/panel/generic/BaseTextInputPanel.java b/src/glum/gui/panel/generic/BaseTextInputPanel.java new file mode 100644 index 0000000..cfd0d7b --- /dev/null +++ b/src/glum/gui/panel/generic/BaseTextInputPanel.java @@ -0,0 +1,163 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.panel.generic; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + +import glum.gui.FocusUtil; +import glum.gui.GuiUtil; +import glum.gui.action.ClickAction; +import glum.gui.component.GTextField; +import glum.gui.panel.GlassPanel; +import net.miginfocom.swing.MigLayout; + +/** + * {@link GlassPanel} used to prompt the user for a text inupt. + *

    + * The title and (prompt) message can be customized. + * + * @author lopeznr1 + */ +public abstract class BaseTextInputPanel extends GlassPanel implements ActionListener, GenericCodes +{ + // Constants + public static final Color warnColor = new Color(128, 0, 0); + + // GUI vars + protected JLabel titleL, inputL, infoL; + protected JButton cancelB, acceptB; + protected GTextField inputTF; + + // State vars + protected boolean isAccepted; + + /** Standard Constructor */ + public BaseTextInputPanel(Component aParent) + { + super(aParent); + + isAccepted = false; + + // Build the actual GUI + buildGuiArea(); + setPreferredSize(new Dimension(300, getPreferredSize().height)); + + // Set up keyboard short cuts + FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(cancelB)); + FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(acceptB)); + FocusUtil.addFocusKeyBinding(inputTF, "ENTER", new ClickAction(acceptB)); + } + + /** + * Returns the input of the user. + */ + public String getInput() + { + if (isAccepted == false) + return null; + + return inputTF.getText(); + } + + /** + * Sets in aStr as the default input + */ + public void setInput(String aStr) + { + inputTF.setValue(aStr); + } + + @Override + public void actionPerformed(ActionEvent aEvent) + { + var source = aEvent.getSource(); + if (source == cancelB) + { + isAccepted = false; + setVisible(false); + notifyListeners(this, ID_CANCEL, "Cancel"); + } + else if (source == acceptB) + { + isAccepted = true; + setVisible(false); + notifyListeners(this, ID_ACCEPT, "Accept"); + } + else if (source == inputTF) + { + updateGui(); + } + } + + @Override + public void setVisible(boolean aBool) + { + if (aBool == true) + isAccepted = false; +// resetGui(); + + super.setVisible(aBool); + } + + /** + * Forms the actual dialog GUI + */ + protected void buildGuiArea() + { + setLayout(new MigLayout("", "[right][grow][][]", "[][][20!][]")); + var tmpFont = (new JTextField()).getFont(); + + // Title Area + titleL = new JLabel("Title", JLabel.CENTER); + add(titleL, "growx,span 4,wrap"); + + // Source area + inputL = new JLabel("Symbol:"); + inputTF = new GTextField(this); + add(inputL); + add(inputTF, "growx,span 3,wrap"); + + // Warn area + var tmpStr = "Please enter text input."; + infoL = GuiUtil.createJLabel(tmpStr, tmpFont); + infoL.setForeground(warnColor); + add(infoL, "growx,span 4,wrap"); + + // Control area + cancelB = GuiUtil.createJButton("Cancel", this); + acceptB = GuiUtil.createJButton("Accept", this); + add(cancelB, "skip 2"); + add(acceptB, ""); + } + + /** + * Sets the Gui and associated components to the initial state + */ + public void resetGui() + { + isAccepted = false; + inputTF.setText(""); + updateGui(); + } + + /** + * Utility method to update the various GUI components (most likely infoL, acceptB) based on the current inputTF. + */ + protected abstract void updateGui(); + +} diff --git a/src/glum/gui/panel/generic/GenericCodes.java b/src/glum/gui/panel/generic/GenericCodes.java index 32daa92..981daad 100644 --- a/src/glum/gui/panel/generic/GenericCodes.java +++ b/src/glum/gui/panel/generic/GenericCodes.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; public interface GenericCodes diff --git a/src/glum/gui/panel/generic/LocationPanel.java b/src/glum/gui/panel/generic/LocationPanel.java index ae86ac3..1a9bd83 100644 --- a/src/glum/gui/panel/generic/LocationPanel.java +++ b/src/glum/gui/panel/generic/LocationPanel.java @@ -1,5 +1,26 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.IOException; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; @@ -8,44 +29,34 @@ import glum.gui.panel.GlassPanel; import glum.io.Loader; import glum.io.LoaderInfo; import glum.unit.ByteUnit; -import glum.unit.Unit; import glum.zio.ZinStream; import glum.zio.ZoutStream; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.io.File; -import java.io.IOException; - -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; - import net.miginfocom.swing.MigLayout; +/** + * {@link GlassPanel} used to prompt the user for a location on the disk. + *

    + * The title and (prompt) message can be customized. + * + * @author lopeznr1 + */ public class LocationPanel extends GlassPanel implements ActionListener, GenericCodes { // Constants - public static final Color warnColor = new Color(128, 0, 0); + public static final Color ColorWarn = new Color(128, 0, 0); // GUI vars - protected JLabel titleL, locationL, infoL, warnL; - protected JButton cancelB, acceptB, fileB; - protected JTextArea instrTA; - protected GTextField locationTF; + private JLabel titleL, locationL, infoL, warnL; + private JButton cancelB, acceptB, fileB; + private JTextArea instrTA; + private GTextField locationTF; // State vars - protected LoaderInfo loaderInfo; - protected long minFreeSpace; - protected boolean isAccepted; + private LoaderInfo loaderInfo; + private long minFreeSpace; + private boolean isAccepted; + /** Standard Constructor */ public LocationPanel(Component aParent) { super(aParent); @@ -65,14 +76,38 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic } /** - * Returns the input of the user. + * Returns the file as specified in the location gui. + *

    + * Returns null if the input is empty. */ - public File getInput() + public File getPath() { - if (isAccepted == false) + var inputStr = locationTF.getText(); + if (inputStr.isEmpty() == true) return null; - return new File(locationTF.getText()); + return new File(inputStr); + } + + /** + * Returns true if this panel was accepted. + */ + public boolean isAccepted() + { + return isAccepted; + } + + /** + * Sets the location gui to reflect the specified file. + */ + public void setPath(File aFile) + { + var inputStr = ""; + if (aFile != null) + inputStr = aFile.getAbsolutePath(); + + locationTF.setText(inputStr); + updateGui(); } /** @@ -102,47 +137,24 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); - if (source == cancelB) - { - isAccepted = false; - setVisible(false); - notifyListeners(this, ID_CANCEL, "Cancel"); - } - else if (source == acceptB) - { - isAccepted = true; - setVisible(false); - notifyListeners(this, ID_ACCEPT, "Accept"); - } - else if (source == locationTF) - { - updateGui(); - } + var source = aEvent.getSource(); + if (source == acceptB) + doActionAccept(); + else if (source == cancelB) + doActionCancel(); else if (source == fileB) - { - File aFile; - - // Retrieve the path to load - aFile = Loader.queryUserForPath(loaderInfo, getParent(), "Select target folder", true); - if (aFile != null) - locationTF.setValue(aFile.getAbsolutePath()); - - updateGui(); - } + doActionDestPath(); + updateGui(); } @Override public void setVisible(boolean aBool) { - // Reset the GUI + // Reset relevant state vars if (aBool == true) { isAccepted = false; - locationTF.setText(""); updateGui(); } @@ -174,12 +186,8 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic */ protected void buildGuiArea() { - JScrollPane tmpPane; - Font aFont; - String aStr; - setLayout(new MigLayout("", "[][][grow]", "[][grow,50::][]")); - aFont = (new JTextField()).getFont(); + var tmpFont = (new JTextField()).getFont(); // Title Area titleL = new JLabel("Title", JLabel.CENTER); @@ -187,7 +195,7 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic // Instruction area instrTA = GuiUtil.createUneditableTextArea(2, 0); - tmpPane = new JScrollPane(instrTA); + var tmpPane = new JScrollPane(instrTA); tmpPane.setBorder(null); add(tmpPane, "growx,growy,span,wrap"); @@ -200,43 +208,74 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic add(locationTF, "growx,span,wrap"); // Info area - aStr = "Please specify the disk location where the catalog should be constructed.."; - infoL = GuiUtil.createJLabel(aStr, aFont); + var tmpStr = "Please specify the disk location where the catalog should be constructed.."; + infoL = GuiUtil.createJLabel(tmpStr, tmpFont); add(infoL, "growx,span,wrap"); // Warn area - aStr = ""; - warnL = GuiUtil.createJLabel(aStr, aFont); - warnL.setForeground(warnColor); + tmpStr = ""; + warnL = GuiUtil.createJLabel(tmpStr, tmpFont); + warnL.setForeground(ColorWarn); add(warnL, "growx,h 20!,span,wrap"); // Control area - cancelB = GuiUtil.createJButton("Cancel", this, aFont); - acceptB = GuiUtil.createJButton("Accept", this, aFont); - add(cancelB, "align right,span,split 2"); + cancelB = GuiUtil.createJButton("Cancel", this); + acceptB = GuiUtil.createJButton("Accept", this); + add(cancelB, "ax right,span,split"); add(acceptB, ""); - - setBorder(new BevelBorder(BevelBorder.RAISED)); } /** - * Utility method to update the various GUI components (most likely infoL, acceptB) based on the current inputTF. + * Helper method to process the "accept" action. */ - protected void updateGui() + private void doActionAccept() { - File destPath, rootPath; - String infoStr, warnStr; - Unit diskUnit; - boolean isValid; - long freeBytes; + isAccepted = true; + setVisible(false); + notifyListeners(this, ID_ACCEPT, "Cancel"); + } + /** + * Helper method to process the "cancel" action. + */ + private void doActionCancel() + { + isAccepted = false; + setVisible(false); + notifyListeners(this, ID_CANCEL, "Cancel"); + } + + /** + * Helper method to process the "specify destination path" action. + */ + private void doActionDestPath() + { + // Retrieve the path to load + File tmpFile = Loader.queryUserForPath(loaderInfo, getParent(), "Select target folder", true); + if (tmpFile != null) + locationTF.setValue(tmpFile.getAbsolutePath()); + } + + /** + * Helper method that keeps various UI components synchronized. + */ + private void updateGui() + { // Retrieve the folder - destPath = null; - if (locationTF.getText().isEmpty() == false) - destPath = new File(locationTF.getText()); + File destPath = null; + String locationStr = locationTF.getText(); + if (locationStr.isEmpty() == false) + { + if (locationStr.equals("~") == true) + locationStr = System.getProperty("user.home"); + if (locationStr.startsWith("~/") == true) + locationStr = new File(System.getProperty("user.home"), locationStr.substring(2)).getAbsolutePath(); + + destPath = new File(locationStr); + } // Retrieve the root folder - rootPath = destPath; + var rootPath = destPath; while (rootPath != null) { if (rootPath.isDirectory() == true) @@ -246,7 +285,8 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic } // Test the validity of the location - isValid = false; + String infoStr, warnStr; + var isValid = false; if (rootPath == null) { infoStr = "Free space: ---"; @@ -254,19 +294,23 @@ public class LocationPanel extends GlassPanel implements ActionListener, Generic } else { - diskUnit = new ByteUnit(2); - freeBytes = rootPath.getFreeSpace(); - infoStr = "Free space: " + diskUnit.getString(freeBytes); + var diskBU = new ByteUnit(2); + var freeBytes = rootPath.getFreeSpace(); + infoStr = "Free space: " + diskBU.getString(freeBytes); if (minFreeSpace > 0) - infoStr += " Required space: " + diskUnit.getString(minFreeSpace); + infoStr += " Required space: " + diskBU.getString(minFreeSpace); warnStr = ""; if (rootPath.canWrite() == false) warnStr = "No write permission at location."; else if (freeBytes < minFreeSpace && minFreeSpace > 0) - warnStr = "Not enough free space on disk. Minimun required: " + diskUnit.getString(minFreeSpace); + warnStr = "Not enough free space on disk. Minimun required: " + diskBU.getString(minFreeSpace); else isValid = true; + + // Update the loaderInfo to reflect the user input + if (destPath != null) + loaderInfo.setPath(destPath); } // Update the components diff --git a/src/glum/gui/panel/generic/MessagePanel.java b/src/glum/gui/panel/generic/MessagePanel.java index 38c1f56..d8e4838 100644 --- a/src/glum/gui/panel/generic/MessagePanel.java +++ b/src/glum/gui/panel/generic/MessagePanel.java @@ -1,31 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.panel.GlassPanel; - -import java.awt.Component; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; - import net.miginfocom.swing.MigLayout; +/** + * {@link GlassPanel} used to provide the user with a notification. + *

    + * The title and (notification) message can be customized. + * + * @author lopeznr1 + */ public class MessagePanel extends GlassPanel implements ActionListener, GenericCodes { // GUI vars - protected JLabel titleL; - protected JTextArea infoTA; - protected JButton closeB; + private JLabel titleL; + private JTextArea infoTA; + private JButton closeB; + /** Standard Constructor */ public MessagePanel(Component aParent, String aTitle, int sizeX, int sizeY) { super(aParent); @@ -39,26 +53,46 @@ public class MessagePanel extends GlassPanel implements ActionListener, GenericC FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB)); } - public MessagePanel(Component aParent) - { - this(aParent, "Untitled", 275, 350); - } - + /** Simplified Constructor */ public MessagePanel(Component aParent, String aTitle) { this(aParent, aTitle, 275, 350); } + /** Simplified Constructor */ + public MessagePanel(Component aParent) + { + this(aParent, "Untitled", 275, 350); + } + /** - * Sets the message of the PromptPanel + * Sets the info message and adjusts the caret position. + */ + public void setInfo(String aStr, int aCaretPos) + { + infoTA.setText(aStr); + infoTA.setCaretPosition(aCaretPos); + } + + /** + * Sets the info message. */ public void setInfo(String aStr) { infoTA.setText(aStr); + infoTA.setCaretPosition(0); } /** - * Sets the title of this PromptPanel + * Sets the tab size associated with the info area. + */ + public void setTabSize(int aSize) + { + infoTA.setTabSize(aSize); + } + + /** + * Sets the title of this panel. */ public void setTitle(String aTitle) { @@ -68,9 +102,7 @@ public class MessagePanel extends GlassPanel implements ActionListener, GenericC @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == closeB) { setVisible(false); @@ -81,14 +113,9 @@ public class MessagePanel extends GlassPanel implements ActionListener, GenericC /** * Forms the actual GUI */ - protected void buildGuiArea() + private void buildGuiArea() { - JScrollPane tmpScrollPane; - Font aFont; - Border aBorder; - setLayout(new MigLayout("", "[right][grow][]", "[][grow][]")); - aFont = (new JTextField()).getFont(); // Title Area titleL = new JLabel("Title", JLabel.CENTER); @@ -99,19 +126,16 @@ public class MessagePanel extends GlassPanel implements ActionListener, GenericC infoTA.setEditable(false); // infoTA.setOpaque(false); infoTA.setLineWrap(true); + infoTA.setTabSize(3); infoTA.setWrapStyleWord(true); - tmpScrollPane = new JScrollPane(infoTA); + JScrollPane tmpScrollPane = new JScrollPane(infoTA); // tmpScrollPane.setBorder(null); add(tmpScrollPane, "growx,growy,span,wrap"); // Control area - closeB = GuiUtil.createJButton("Close", this, aFont); - add(closeB, "skip 2,span 1"); - - // Border - aBorder = new BevelBorder(BevelBorder.RAISED); - setBorder(aBorder); + closeB = GuiUtil.createJButton("Close", this); + add(closeB, "skip 2"); } } diff --git a/src/glum/gui/panel/generic/PromptPanel.java b/src/glum/gui/panel/generic/PromptPanel.java index e373fe3..df6181c 100644 --- a/src/glum/gui/panel/generic/PromptPanel.java +++ b/src/glum/gui/panel/generic/PromptPanel.java @@ -1,59 +1,76 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.panel.GlassPanel; - -import java.awt.Component; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextArea; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; - import net.miginfocom.swing.MigLayout; +/** + * {@link GlassPanel} used to prompt the user of an action to be taken. A message will be provided via a + * {@link JTextArea}. + *

    + * The title and (prompt) message can be customized. + * + * @author lopeznr1 + */ public class PromptPanel extends GlassPanel implements ActionListener, GenericCodes { // GUI vars - protected JLabel titleL; - protected JTextArea infoTA; - protected JButton cancelB, acceptB; - - // State vars - protected boolean isAccepted; + private JLabel titleL; + private JTextArea infoTA; + private JButton cancelB, acceptB; + // State vars + private boolean isAccepted; + + /** Standard Constructor */ public PromptPanel(Component aParent, String aTitle, int sizeX, int sizeY) { super(aParent); - + isAccepted = false; buildGuiArea(); setSize(sizeX, sizeY); setTitle(aTitle); - + // Set up keyboard short cuts FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(cancelB)); FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(acceptB)); } - public PromptPanel(Component aParent) - { - this(aParent, "Untitled", 275, 350); - } - + /** Simplified Constructor */ public PromptPanel(Component aParent, String aTitle) { this(aParent, aTitle, 275, 350); } - + + /** Simplified Constructor */ + public PromptPanel(Component aParent) + { + this(aParent, "Untitled", 275, 350); + } + /** * Returns true if the prompt was accepted */ @@ -61,9 +78,18 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo { return isAccepted; } - + /** - * Sets the message of the PromptPanel + * Sets the info message and adjusts the caret position. + */ + public void setInfo(String aStr, int aCaretPos) + { + infoTA.setText(aStr); + infoTA.setCaretPosition(aCaretPos); + } + + /** + * Sets the info message. */ public void setInfo(String aStr) { @@ -71,7 +97,15 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo } /** - * Sets the title of this PromptPanel + * Sets in the tabs size of the info text area. + */ + public void setTabSize(int aNumSpaces) + { + infoTA.setTabSize(aNumSpaces); + } + + /** + * Sets the title of this panel. */ public void setTitle(String aTitle) { @@ -81,9 +115,7 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == cancelB) { isAccepted = false; @@ -97,14 +129,14 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo notifyListeners(this, ID_ACCEPT, "Accept"); } } - + @Override public void setVisible(boolean isVisible) { // Reset the panel if (isVisible == true) isAccepted = false; - + super.setVisible(isVisible); } @@ -113,17 +145,12 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo */ protected void buildGuiArea() { - JScrollPane tmpScrollPane; - Font aFont; - Border aBorder; - setLayout(new MigLayout("", "[right][grow][][]", "[][grow][]")); - aFont = (new JTextField()).getFont(); - + // Title Area titleL = new JLabel("Title", JLabel.CENTER); add(titleL, "growx,span,wrap"); - + // Info area infoTA = new JTextArea("No status", 3, 0); infoTA.setEditable(false); @@ -131,19 +158,15 @@ public class PromptPanel extends GlassPanel implements ActionListener, GenericCo infoTA.setLineWrap(true); infoTA.setWrapStyleWord(true); - tmpScrollPane = new JScrollPane(infoTA); + var tmpScrollPane = new JScrollPane(infoTA); // tmpScrollPane.setBorder(null); add(tmpScrollPane, "growx,growy,span,wrap"); - + // Control area - cancelB = GuiUtil.createJButton("Cancel", this, aFont); - acceptB = GuiUtil.createJButton("Accept", this, aFont); + cancelB = GuiUtil.createJButton("Cancel", this); + acceptB = GuiUtil.createJButton("Accept", this); add(cancelB, "skip 2"); add(acceptB, ""); - - // Border - aBorder = new BevelBorder(BevelBorder.RAISED); - setBorder(aBorder); } } diff --git a/src/glum/gui/panel/generic/SimplePromptPanel.java b/src/glum/gui/panel/generic/SimplePromptPanel.java index 9aef442..b39d0c6 100644 --- a/src/glum/gui/panel/generic/SimplePromptPanel.java +++ b/src/glum/gui/panel/generic/SimplePromptPanel.java @@ -1,16 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; -import java.awt.*; -import java.awt.event.*; -import javax.swing.*; -import javax.swing.border.*; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; -import glum.gui.*; +import javax.swing.*; + +import glum.gui.FocusUtil; +import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.panel.GlassPanel; -import glum.gui.panel.generic.GenericCodes; import net.miginfocom.swing.MigLayout; +/** + * {@link GlassPanel} used to prompt the user of an action to be taken. A message will be provided via a {@link JLabel}. + *

    + * The title and (prompt) message can be customized. + * + * @author lopeznr1 + */ public class SimplePromptPanel extends GlassPanel implements ActionListener, GenericCodes { // GUI vars @@ -20,6 +42,7 @@ public class SimplePromptPanel extends GlassPanel implements ActionListener, Gen // State vars protected boolean isAccepted; + /** Standard Constructor */ public SimplePromptPanel(Component aParent) { super(aParent); @@ -62,9 +85,7 @@ public class SimplePromptPanel extends GlassPanel implements ActionListener, Gen @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == cancelB) { isAccepted = false; @@ -91,26 +112,22 @@ public class SimplePromptPanel extends GlassPanel implements ActionListener, Gen */ protected void buildGuiArea() { - Font aFont; - setLayout(new MigLayout("", "[right][grow][][]", "[][][20!][]")); - aFont = (new JTextField()).getFont(); + var tmpFont = (new JTextField()).getFont(); // Title Area titleL = new JLabel("Title", JLabel.CENTER); add(titleL, "growx,span,wrap"); // Message area - messageL = GuiUtil.createJLabel("Message", aFont); + messageL = GuiUtil.createJLabel("Message", tmpFont); add(messageL, "growx,span,wrap"); // Control area - cancelB = GuiUtil.createJButton("Cancel", this, aFont); - acceptB = GuiUtil.createJButton("Accept", this, aFont); + cancelB = GuiUtil.createJButton("Cancel", this); + acceptB = GuiUtil.createJButton("Accept", this); add(cancelB, "skip 2"); add(acceptB, ""); - - setBorder(new BevelBorder(BevelBorder.RAISED)); } } diff --git a/src/glum/gui/panel/generic/TextInputPanel.java b/src/glum/gui/panel/generic/TextInputPanel.java index e79c8d3..2d868a3 100644 --- a/src/glum/gui/panel/generic/TextInputPanel.java +++ b/src/glum/gui/panel/generic/TextInputPanel.java @@ -1,153 +1,168 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.generic; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.*; -import javax.swing.border.BevelBorder; -import glum.gui.FocusUtil; -import glum.gui.GuiUtil; -import glum.gui.action.ClickAction; -import glum.gui.component.GTextField; -import glum.gui.panel.GlassPanel; -import net.miginfocom.swing.MigLayout; +import java.awt.Component; +import java.util.Collection; +import java.util.regex.Pattern; -public abstract class TextInputPanel extends GlassPanel implements ActionListener, GenericCodes +import com.google.common.collect.ImmutableSet; + +/** + * UI component that provides the user with a simple prompt to allow for simple textual input. + *

    + * Support is provided for the following: + *

      + *
    • Checking for empty input. This is treated as an error. + *
    • Checking for input that matches reserved input. This is treated as an error. + *
    • Checking for input that matches utilized input. This is treated as a warning. + *
    + * + * @author lopeznr1 + */ +public class TextInputPanel extends BaseTextInputPanel { - // Constants - public static final Color warnColor = new Color(128, 0, 0); - - // GUI vars - protected JLabel titleL, inputL, infoL; - protected JButton cancelB, acceptB; - protected GTextField inputTF; - // State vars - protected boolean isAccepted; + private ImmutableSet reservedS; + private ImmutableSet utilizedS; + private Pattern matchPattern; + + private String issueBadInputMsg; + private String issueNoInputMsg; + private String issueReservedMsg; + private String issueUtilizedMsg; /** - * Constructor + * Standard Constructor */ - public TextInputPanel(Component aParent) + public TextInputPanel(Component aParent, String aTitle, String aNameLabel) { super(aParent); - - isAccepted = false; - // Build the actual GUI - buildGuiArea(); - setPreferredSize(new Dimension(300, getPreferredSize().height)); + // Set in a more specific title and input label + titleL.setText(aTitle); + inputL.setText(aNameLabel); - // Set up keyboard short cuts - FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(cancelB)); - FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(acceptB)); - FocusUtil.addFocusKeyBinding(inputTF, "ENTER", new ClickAction(acceptB)); - } - - /** - * Returns the input of the user. - */ - public String getInput() - { - if (isAccepted == false) - return null; - - return inputTF.getText(); - } + reservedS = ImmutableSet.of(); + utilizedS = ImmutableSet.of(); + matchPattern = null; - /** - * Sets in aStr as the default input - */ - public void setInput(String aStr) - { - inputTF.setValue(aStr); - } + issueNoInputMsg = "Specified input is invalid."; + issueBadInputMsg = "Please enter a valid name."; + issueReservedMsg = "Name is reserved. Please pick another."; + issueUtilizedMsg = "Name is in use. Item will be overwritten"; - @Override - public void actionPerformed(ActionEvent aEvent) - { - Object source; - - source = aEvent.getSource(); - if (source == cancelB) - { - isAccepted = false; - setVisible(false); - notifyListeners(this, ID_CANCEL, "Cancel"); - } - else if (source == acceptB) - { - isAccepted = true; - setVisible(false); - notifyListeners(this, ID_ACCEPT, "Accept"); - } - else if (source == inputTF) - { - updateGui(); - } - } - - @Override - public void setVisible(boolean aBool) - { - if (aBool == true) - isAccepted = false; -// resetGui(); - - super.setVisible(aBool); - } - - /** - * Forms the actual dialog GUI - */ - protected void buildGuiArea() - { - Font aFont; - String aStr; - - setLayout(new MigLayout("", "[right][grow][][]", "[][][20!][]")); - aFont = (new JTextField()).getFont(); - - // Title Area - titleL = new JLabel("Title", JLabel.CENTER); - add(titleL, "growx,span 4,wrap"); - - // Source area - inputL = new JLabel("Symbol:"); - inputTF = new GTextField(this); - add(inputL); - add(inputTF, "growx,span 3,wrap"); - - // Warn area - aStr = "Please enter text input."; - infoL = GuiUtil.createJLabel(aStr, aFont); - infoL.setForeground(warnColor); - add(infoL, "growx,span 4,wrap"); - - // Control area - cancelB = GuiUtil.createJButton("Cancel", this, aFont); - acceptB = GuiUtil.createJButton("Accept", this, aFont); - add(cancelB, "skip 2,span 1"); - add(acceptB, "span 1"); - - setBorder(new BevelBorder(BevelBorder.RAISED)); - } - - /** - * Sets the Gui and associated components to the initial state - */ - public void resetGui() - { - isAccepted = false; - inputTF.setText(""); updateGui(); } - + /** - * Utility method to update the various GUI components - * (most likely infoL, acceptB) based on the current - * inputTF. + * Simplified Constructor */ - protected abstract void updateGui(); + public TextInputPanel(Component aParent, String aTitle) + { + this(aParent, aTitle, "Name:"); + } + + /** + * Method that allows the user to customize the warning / error message associated with user input. + * + * @param aIssueBadInputMsg + * Message used when the user has entered bad input. + * @param aIssueNoInputMsg + * Message used when the user has not entered any input. + * @param aIssueReservedMsg + * Message used when the user has entered input that is reserved. + * @param aIssueUtilizedMsg + * Message used when the user has entered input that is utilized. + */ + public void setIssueMessages(String aIssueBadInputMsg, String aIssueNoInputMsg, String aIssueReservedMsg, + String aIssueUtilizedMsg) + { + if (aIssueBadInputMsg != null) + issueBadInputMsg = aIssueBadInputMsg; + if (aIssueNoInputMsg != null) + issueNoInputMsg = aIssueNoInputMsg; + if (aIssueReservedMsg != null) + issueReservedMsg = aIssueReservedMsg; + if (aIssueUtilizedMsg != null) + issueUtilizedMsg = aIssueUtilizedMsg; + + updateGui(); + } + + /** + * Sets in a regular expression for which to check for valid input. + *

    + * User specified input must match this regular expression. + *

    + * Passing in null will allow any user input to match. + */ + public void setMatchRegex(String aMatchRegex) + { + matchPattern = null; + if (aMatchRegex != null) + matchPattern = Pattern.compile(aMatchRegex); + + updateGui(); + } + + /** + * Sets in all of the names that have been reserved. + *

    + * Input matching items in this list will not be allowed. + */ + public void setReservedNames(Collection aReservedC) + { + reservedS = ImmutableSet.copyOf(aReservedC); + updateGui(); + } + + /** + * Sets in all of the names that have been utilized. + *

    + * Input matching items in this list will be allowed - but will result in a warning. + */ + public void setUtilizedNames(Collection aUtilizedC) + { + utilizedS = ImmutableSet.copyOf(aUtilizedC); + updateGui(); + } + + @Override + protected void updateGui() + { + String inputStr = inputTF.getText(); + + // Check for errors / warnings + String failMsg = null; + String warnMsg = null; + if (inputStr.equals("") == true) + failMsg = issueNoInputMsg; + else if (matchPattern != null && matchPattern.matcher(inputStr).matches() == false) + failMsg = issueBadInputMsg; + else if (reservedS.contains(inputStr) == true) + failMsg = issueReservedMsg; + else if (utilizedS.contains(inputStr) == true) + warnMsg = issueUtilizedMsg; + + String infoMsg = failMsg; + if (infoMsg == null) + infoMsg = warnMsg; + infoL.setText(infoMsg); + + boolean isEnabled = failMsg == null; + acceptB.setEnabled(isEnabled); + } } diff --git a/src/glum/gui/panel/itemList/BasicItemHandler.java b/src/glum/gui/panel/itemList/BasicItemHandler.java deleted file mode 100644 index ff5671f..0000000 --- a/src/glum/gui/panel/itemList/BasicItemHandler.java +++ /dev/null @@ -1,569 +0,0 @@ -package glum.gui.panel.itemList; - -import glum.gui.panel.itemList.query.QueryAttribute; -import glum.gui.panel.itemList.query.QueryComposer; -import glum.gui.panel.itemList.query.QueryTableCellRenderer; -import glum.unit.Unit; -import glum.unit.UnitListener; -import glum.unit.UnitProvider; -import glum.zio.*; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Map; - -import javax.swing.JTable; -import javax.swing.table.JTableHeader; -import javax.swing.table.TableCellRenderer; -import javax.swing.table.TableColumn; -import javax.swing.table.TableColumnModel; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -public abstract class BasicItemHandler implements ZioObj, ItemHandler, UnitListener -{ - protected JTable myOwner; - protected ArrayList fullAttributeList; - protected ArrayList sortedAttributeList; - - /** - * Constructor - */ - public BasicItemHandler(QueryComposer aComposer) - { - this(aComposer.getItems()); - } - - public BasicItemHandler(Collection aQueryAttrList) - { - int evalIndex; - - myOwner = null; - - fullAttributeList = new ArrayList(); - sortedAttributeList = new ArrayList(); - - if (aQueryAttrList != null && aQueryAttrList.isEmpty() == false) - { - evalIndex = 0; - for (QueryAttribute aAttr : aQueryAttrList) - { - // Ensure the model index is appropriately initialized - if (evalIndex != aAttr.modelIndex) - throw new RuntimeException("Improper initialization. Expected Index: " + evalIndex + " Received Index: " + aAttr.modelIndex); - - fullAttributeList.add(aAttr); - sortedAttributeList.add(aAttr); - evalIndex++; - - // Register for the appropriate unit events - aAttr.refUnitProvider.addListener(this); - } - } - - fullAttributeList.trimToSize(); - sortedAttributeList.trimToSize(); - } - - @Override - public void zioRead(ZinStream aStream) throws IOException - { - ArrayList newSortedList; - int numItems, index; - - // Header - aStream.readVersion(0); - - // Payload - ZioObjUtil.readList(aStream, fullAttributeList); - - // Reorder the sortedAttributeList based on the serialization - numItems = aStream.readInt(); - newSortedList = Lists.newArrayListWithCapacity(numItems); - for (int c1 = 0; c1 < numItems; c1++) - { - index = aStream.readInt(); - newSortedList.add(fullAttributeList.get(index)); - } - - sortedAttributeList = newSortedList; - sortedAttributeList.trimToSize(); - - // Initialize the table columns - rebuildTableColumns(); - } - - @Override - public void zioWrite(ZoutStream aStream) throws IOException - { - int numItems; - - // Header - aStream.writeVersion(0); - - // Payload - ZioObjUtil.writeList(aStream, fullAttributeList); - - // Output the order of the sortedAttributeList - numItems = sortedAttributeList.size(); - aStream.writeInt(numItems); - for (QueryAttribute aAttr : sortedAttributeList) - aStream.writeInt(aAttr.modelIndex); - } - - @Override - public void initialize(JTable aOwner) - { - JTableHeader aTableHeader; - TableColumnModel aTableColumnModel; - TableColumn aTableColumn; - - // This method is only allowed to be called once! - if (myOwner != null) - throw new RuntimeException("QueryItemHandler already initialized!"); - - myOwner = aOwner; - - aTableHeader = myOwner.getTableHeader(); - aTableColumnModel = aTableHeader.getColumnModel(); - - // Customize overall settings - aTableHeader.setReorderingAllowed(false); - - // Grab all of the precomputed columns from the table - // and store with their associated queryAttributes - for (int c1 = 0; c1 < fullAttributeList.size(); c1++) - { - aTableColumn = aTableColumnModel.getColumn(c1); - fullAttributeList.get(c1).assocTableColumn = aTableColumn; - } - - // Rebuild the table columns; Needed so that only the - // visible ones are displayed. Do this only after you - // have grabbed the table columns as aTableColumnModel - // will strictly contain the "visible" ones - rebuildTableColumns(); - } - - @Override - public int getColumnCount() - { - return fullAttributeList.size(); - } - - @Override - public Class getColumnClass(int colNum) - { - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return String.class; - - return fullAttributeList.get(colNum).refClass; - } - - public int getColumnDefaultWidth(int colNum) - { - int defaultSize, minSize; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return -1; - - // Get the default and min size - defaultSize = fullAttributeList.get(colNum).defaultSize; - minSize = fullAttributeList.get(colNum).minSize; - - // Ensure size makes sense - if (defaultSize < minSize) - return minSize; - - return defaultSize; - } - - /** - * getColumnMinWidth - */ -/* public int getColumnMinWidth(int colNum) - { - // Insanity check - if (queryAttributes == null) - return -1; - - if (colNum < 0 && colNum >= queryAttributes.length) - return -1; - - return queryAttributes[colNum].minSize; - } -*/ - /** - * getColumnMaxWidth - */ - public int getColumnMaxWidth(int colNum) - { - int defaultSize, maxSize; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return -1; - - // Get the default and max size - defaultSize = fullAttributeList.get(colNum).defaultSize; - maxSize = fullAttributeList.get(colNum).maxSize; - - // Ensure size makes sense - if (defaultSize > maxSize && maxSize != -1) - return defaultSize; - - return maxSize; - } - - @Override - public String getColumnLabel(int colNum) - { - QueryAttribute aAttribute; - Unit aUnit; - String aStr; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return ""; - - aAttribute = fullAttributeList.get(colNum); - - // Retrieve the associated unit - aUnit = aAttribute.refUnitProvider.getUnit(); - - // Retrieve the base column label - aStr = aAttribute.label; - - // Append the unit name to the column label - if (aUnit != null && "".equals(aUnit.getLabel(false)) == false) - return aStr + " [" + aUnit.getLabel(false) + "]"; - else - return aStr; - } - - @Override - public Collection getColumnLabels() - { - Collection retSet; - - if (fullAttributeList == null) - return new ArrayList(); - - retSet = new ArrayList(); - for (QueryAttribute aAttribute : fullAttributeList) - { - if (aAttribute != null && aAttribute.label != null) - retSet.add(aAttribute.label); - else - retSet.add(""); - } - - return retSet; - } - - @Override - public boolean isCellEditable(int colNum) - { - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return false; - - if (fullAttributeList.get(colNum).editor == null) - return false; - - return true; - } - - @Override - public boolean isColumnVisible(int colNum) - { - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return false; - - return fullAttributeList.get(colNum).isVisible; - } - - /** - * update - */ - public void update() - { - TableColumn aTableColumn; - QueryTableCellRenderer aRenderer; - Object aObject; - Unit aUnit; - - // Update all the TableColumn renderers with the appropriate unit - for (QueryAttribute aAttribute : fullAttributeList) - { - if (aAttribute.assocTableColumn != null) - { - aTableColumn = aAttribute.assocTableColumn; - aObject = aTableColumn.getCellRenderer(); - if (aObject instanceof QueryTableCellRenderer) - { - aRenderer = (QueryTableCellRenderer)aObject; - - aAttribute.assocTableColumn.setHeaderValue( getColumnLabel(aAttribute.modelIndex) ); - - aUnit = aAttribute.refUnitProvider.getUnit(); - aRenderer.setUnit(aUnit); - } - } - } - } - - @Override - public void unitChanged(UnitProvider aManager, String aKey) - { - JTableHeader aTableHeader; - - update(); - - myOwner.repaint(); - - aTableHeader = myOwner.getTableHeader(); - if (aTableHeader != null) - aTableHeader.repaint(); -/* - for (QueryAttribute aAttribute : queryAttributes) - { - if (aKey.equals(aAttribute.unitKey) == true) - } - - Tile aTile; - - // Update our listPanel to by sync with the active tile - if (aKey.equals("tile.active") == true) - { - aTile = refRegistry.getSingleton(aKey, Tile.class); - - listPanel.removeListSelectionListener(this); - listPanel.selectItem(aTile); - listPanel.addListSelectionListener(this); - } - - updateGui(); -*/ - } - - - -public Unit getUnit(int colNum) -{ - QueryAttribute aAttribute; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return null; - - aAttribute = fullAttributeList.get(colNum); - return aAttribute.refUnitProvider.getUnit(); -} - - -public void setColumnAlignment(int colNum, int aAlignment) -{ - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - fullAttributeList.get(colNum).alignment = aAlignment; -} - -public void setColumnLabel(int colNum, String aLabel) -{ - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - fullAttributeList.get(colNum).label = aLabel; -} - -public void setColumnPosition(int colNum, int aPosition) -{ - // Insanity check - if (colNum < 0 || aPosition < 0 - || colNum >= fullAttributeList.size() - || aPosition >= fullAttributeList.size()) - return; - - sortedAttributeList.remove(fullAttributeList.get(colNum)); - sortedAttributeList.add(aPosition, fullAttributeList.get(colNum)); -} - - -public void setColumnSize(int colNum, int aSize) -{ - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - -System.out.println("[QueryItemHandler.java] Changing size of colNum: " + aSize); - fullAttributeList.get(colNum).defaultSize = aSize; -} - - -public void setColumnSortDir(int colNum, int aSortDir) -{ - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - fullAttributeList.get(colNum).sortDir = aSortDir; -} - - -public void setColumnVisible(int colNum, boolean isVisible) -{ - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - fullAttributeList.get(colNum).isVisible = isVisible; - - // Update our table - if (myOwner != null) - rebuildTableColumns(); -} - - - - - - - - - - /** - * getSortedAttributes - */ - public ArrayList getSortedAttributes() - { - return new ArrayList(sortedAttributeList); - } - - - /** - * moveSortedAttribute - */ - public void moveSortedAttribute(int currIndex, int newIndex) - { - QueryAttribute aItem; - - aItem = sortedAttributeList.get(currIndex); - sortedAttributeList.remove(currIndex); - sortedAttributeList.add(newIndex, aItem); - } - - /** - * Method to reconfigure the columns of the table. This will update the - * display order of the columns as well as the individual relevant - * attributes of the column. Currently supported relevant attributes are - * those defined in the method {@link QueryAttribute#setConfig}. - * - * Note any non specified columns will appear last according to the previous order. - * - * @param orderSet: Ordered set of QueryAttributes with matching modelIndexes - */ - public void setOrderAndConfig(Collection orderArr) - { - Map itemMap; - QueryAttribute workItem; - - // Form a lookup map (modelIndex to attribute) - itemMap = Maps.newLinkedHashMap(); - for (QueryAttribute aItem : sortedAttributeList) - itemMap.put(aItem.modelIndex, aItem); - - // Rebuild the sortedQueryAttribute list to conform with - // - the specified order of orderList - // - synch up relevant attributes - sortedAttributeList.clear(); - for (QueryAttribute aItem: orderArr) - { - workItem = itemMap.remove(aItem.modelIndex); - if (workItem != null) - { - workItem.setConfig(aItem); - sortedAttributeList.add(workItem); - } - } - - sortedAttributeList.addAll(itemMap.values()); - itemMap.clear(); - } - - /** - * initializeTableColumn - Helper method to initialize aTableColumn - * with the actual QueryAttribute properties. - * - * @param colNum: Index into column model - */ - protected void initializeTableColumn(int colNum) - { - TableCellRenderer aRenderer; - QueryAttribute aAttribute; - TableColumn aTableColumn; - String aLabel; - int defaultWidth, maxWidth, minWidth; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - // Get the associated table column - aAttribute = fullAttributeList.get(colNum); - aTableColumn = aAttribute.assocTableColumn; - if (aTableColumn == null) - return; - - // Retrieve settings of interest - aLabel = getColumnLabel(colNum); - defaultWidth = getColumnDefaultWidth(colNum); - maxWidth = getColumnMaxWidth(colNum); - minWidth = aAttribute.minSize; - - // Set up the column's renderer - aRenderer = aAttribute.renderer; - if (aRenderer == null) - aRenderer = new QueryTableCellRenderer(aAttribute); - aTableColumn.setCellRenderer(aRenderer); - - // Set up the column's editor - aTableColumn.setCellEditor(aAttribute.editor); - - // Set up the column's size attributes - aTableColumn.setMinWidth(minWidth); - aTableColumn.setMaxWidth(maxWidth); - aTableColumn.setPreferredWidth(defaultWidth); - - // Set up the column header - aTableColumn.setHeaderValue(aLabel); - } - -// TODO -> This should probably be protected - public void rebuildTableColumns() - { - // Enforce the constraints (QueryAttribute) on the associated columns - for (int c1 = 0; c1 < fullAttributeList.size(); c1++) - initializeTableColumn(c1); - - // Remove all of the columns from the table - for (QueryAttribute aAttribute : fullAttributeList) - myOwner.removeColumn(aAttribute.assocTableColumn); - - // Add in only the columns that are visible - for (QueryAttribute aAttribute : sortedAttributeList) - { - if (aAttribute.isVisible == true) - myOwner.addColumn(aAttribute.assocTableColumn); - } - } - -} diff --git a/src/glum/gui/panel/itemList/BasicItemProcessor.java b/src/glum/gui/panel/itemList/BasicItemProcessor.java index d93441d..c430a61 100644 --- a/src/glum/gui/panel/itemList/BasicItemProcessor.java +++ b/src/glum/gui/panel/itemList/BasicItemProcessor.java @@ -1,26 +1,46 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; import java.util.*; +import glum.item.*; + +/** + * Base implementation of the interface {@link ItemProcessor}. + * + * @author lopeznr1 + */ public abstract class BasicItemProcessor implements ItemProcessor { - private Collection myListeners; + private final List listenerL; public BasicItemProcessor() { - myListeners = new ArrayList(); + listenerL = new ArrayList<>(); } @Override - public synchronized void addItemChangeListener(ItemChangeListener aListener) + public synchronized void addListener(ItemEventListener aListener) { - myListeners.add(aListener); + listenerL.add(aListener); } @Override - public synchronized void removeItemChangeListener(ItemChangeListener aListener) + public synchronized void delListener(ItemEventListener aListener) { - myListeners.remove(aListener); + listenerL.remove(aListener); } /** @@ -28,17 +48,16 @@ public abstract class BasicItemProcessor implements ItemProcessor */ protected void notifyListeners() { - Collection notifySet; - // Get the listeners - synchronized(this) + Collection tmpListenerL; + synchronized (this) { - notifySet = new ArrayList(myListeners); + tmpListenerL = new ArrayList<>(listenerL); } // Send out the notifications - for (ItemChangeListener aListener : notifySet) - aListener.itemChanged(); + for (ItemEventListener aListener : tmpListenerL) + aListener.handleItemEvent(this, ItemEventType.ItemsChanged); } } diff --git a/src/glum/gui/panel/itemList/FilterItemProcessor.java b/src/glum/gui/panel/itemList/FilterItemProcessor.java index 9977431..a4c9009 100644 --- a/src/glum/gui/panel/itemList/FilterItemProcessor.java +++ b/src/glum/gui/panel/itemList/FilterItemProcessor.java @@ -1,26 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; import java.util.ArrayList; import java.util.Collection; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import glum.filter.Filter; import glum.filter.NullFilter; +/** + * Implementation of {@link ItemProcessor} which provides automatic filtering. + *

    + * Only items that pass the specified activeFilter will be returned. + * + * @author lopeznr1 + */ public class FilterItemProcessor extends BasicItemProcessor { // State vars - private ArrayList fullList; - private ArrayList passList; + private ImmutableList fullItemL; + private ImmutableList passItemL; private Filter activeFilter; + /** Standard Constructor */ public FilterItemProcessor() { - super(); - - fullList = Lists.newArrayList(); - passList = Lists.newArrayList(); + fullItemL = ImmutableList.of(); + passItemL = ImmutableList.of(); activeFilter = new NullFilter(); } @@ -51,9 +70,9 @@ public class FilterItemProcessor extends BasicItemProcessor * Replaces the current full list of items stored with aItemList. Note that the number of items available by this * processor may be less than the number of items in aItemList due to the active filter. */ - public void setItems(Collection aItemList) + public void setItems(Collection aItemC) { - fullList = new ArrayList(aItemList); + fullItemL = ImmutableList.copyOf(aItemC); rebuildPassList(); // Notify our listeners @@ -63,13 +82,13 @@ public class FilterItemProcessor extends BasicItemProcessor @Override public int getNumItems() { - return passList.size(); + return passItemL.size(); } @Override - public Collection getItems() + public ImmutableList getAllItems() { - return Lists.newArrayList(passList); + return passItemL; } /** @@ -78,15 +97,14 @@ public class FilterItemProcessor extends BasicItemProcessor */ private void rebuildPassList() { - passList = Lists.newArrayList(); - - for (G1 aItem : fullList) + var tmpItemL = new ArrayList(); + for (var aItem : fullItemL) { if (activeFilter == null || activeFilter.isValid(aItem) == true) - passList.add(aItem); + tmpItemL.add(aItem); } - passList.trimToSize(); + passItemL = ImmutableList.copyOf(tmpItemL); } } diff --git a/src/glum/gui/panel/itemList/ItemChangeListener.java b/src/glum/gui/panel/itemList/ItemChangeListener.java deleted file mode 100644 index e8a90a4..0000000 --- a/src/glum/gui/panel/itemList/ItemChangeListener.java +++ /dev/null @@ -1,10 +0,0 @@ -package glum.gui.panel.itemList; - -public interface ItemChangeListener -{ - /** - * ItemChangeListener interface methods - */ - public void itemChanged(); - -} diff --git a/src/glum/gui/panel/itemList/ItemHandler.java b/src/glum/gui/panel/itemList/ItemHandler.java index 8588bdc..18fbd75 100644 --- a/src/glum/gui/panel/itemList/ItemHandler.java +++ b/src/glum/gui/panel/itemList/ItemHandler.java @@ -1,31 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.util.*; - -import javax.swing.*; - -public interface ItemHandler +/** + * Interface which allows standardized mechanism for getting and setting of values associated with an item. + * + * @param + * Generic associated with the item. + * @param + * Generic enum that defines the "names" of values to be accessed (get / set). + * + * @author lopeznr1 + */ +public interface ItemHandler> { /** - * ItemHandler interface methods - */ - public int getColumnCount(); - public Class getColumnClass(int colNum); - public String getColumnLabel(int colNum); - public Collection getColumnLabels(); - public Object getColumnValue(G1 aItem, int colNum); - public void setColumnValue(G1 aItem, int colNum, Object aValue); - public boolean isCellEditable(int colNum); - public boolean isColumnVisible(int colNum); + * Returns the appropriate data field within the item for the specified lookup enum. + * + * @param aItem + * @param aEnum + */ + public Object getValue(G1 aItem, G2 aEnum); /** - * Notifies the ItemHandler of its associated JTable This table should be updated or painted whenever - * the internals of this ItemHandler change + * Updates the appropriate data field within the item for the specified lookup enum (with the provided value). + * + * @param aItem + * @param aEnum + * @param aValue */ - public void initialize(JTable aOwner); - - /** - * Notifies the ItemHandler to synchronize the items to match the state of the associated Columns - */ -// public void synchItems(); + public void setValue(G1 aItem, G2 aEnum, Object aValue); + } diff --git a/src/glum/gui/panel/itemList/ItemListPanel.java b/src/glum/gui/panel/itemList/ItemListPanel.java index 2c0df06..d958ed0 100644 --- a/src/glum/gui/panel/itemList/ItemListPanel.java +++ b/src/glum/gui/panel/itemList/ItemListPanel.java @@ -1,69 +1,110 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.event.*; -import java.util.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; + import javax.swing.*; -import javax.swing.event.*; -import javax.swing.table.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.TableColumn; +import glum.gui.GuiExeUtil; +import glum.gui.TableUtil; import glum.gui.component.GComboBox; -import glum.gui.table.*; - -import com.google.common.collect.*; +import glum.gui.panel.itemList.query.QueryComposer; +import glum.gui.table.JTableScrolling; +import glum.gui.table.TableSorter; +import glum.item.*; import net.miginfocom.swing.MigLayout; -public class ItemListPanel extends JPanel implements ActionListener, ListSelectionListener, ItemChangeListener +/** + * UI component that will display a list of items as provided by the specified {@link ItemHandler} and + * {@link ItemProcessor}. + * + * @author lopeznr1 + */ +public class ItemListPanel> extends JPanel + implements ActionListener, ItemEventListener, ListSelectionListener { - // Gui components - protected JTable myTable; - protected JScrollPane tableScrollPane; - protected ItemListTableModel myTableModel; - protected TableSorter sortTableModel; - protected GComboBox searchBox; - protected JTextField searchTF; + // Ref vars + private final ItemProcessor refItemProcessor; + + // Gui vars + private JTable myTable; + private JScrollPane tableScrollPane; + private TableSorter sortTableModel; + private TableColumnHandler workTableColumnHandler; + private ItemListTableModel viewTableModel; + private GComboBox searchBox; + private JTextField searchTF; // State vars - protected ItemHandler myItemHandler; - protected ItemProcessor myItemProcessor; - protected boolean updateNeeded; + private List listenerL; + private boolean updateNeeded; - // Communicator vars - protected List myListeners; - - public ItemListPanel(ItemHandler aItemHandler, ItemProcessor aItemProcessor, boolean hasSearchBox, boolean supportsMultipleSelection) + /** Standard Constructor */ + public ItemListPanel(ItemHandler aItemHandler, ItemProcessor aItemProcessor, QueryComposer aComposer, + boolean aSupportsMultipleSelection) { - // State vars - myItemHandler = aItemHandler; - myItemProcessor = aItemProcessor; + // Delegate + this(aItemHandler, aItemProcessor, aComposer, false, aSupportsMultipleSelection); + } + + private ItemListPanel(ItemHandler aItemHandler, ItemProcessor aItemProcessor, + QueryComposer aComposer, boolean aHasSearchBox, boolean aSupportsMultipleSelection) + { + refItemProcessor = aItemProcessor; + + listenerL = new ArrayList<>(); updateNeeded = true; - // Communicator vars - myListeners = Lists.newLinkedList(); + // Form the gui + buildGuiArea(aItemHandler, aComposer, aHasSearchBox, aSupportsMultipleSelection); + GuiExeUtil.executeOnceWhenShowing(myTable, () -> updateTable()); - // Build the actual GUI - buildGuiArea(hasSearchBox, supportsMultipleSelection); - - // Register for DataChange events and trigger the initial one - myItemProcessor.addItemChangeListener(this); + // Register for events of interest + refItemProcessor.addListener(this); } /** - * addListSelectionListener + * Registers a ListSelectionListener with this ItemListPanel. */ public synchronized void addListSelectionListener(ListSelectionListener aListener) { - myListeners.add(aListener); + listenerL.add(aListener); } /** - * removeListSelectionListener + * Deregisters a ListSelectionListener with this ItemListPanel. */ - public synchronized void removeListSelectionListener(ListSelectionListener aListener) + public synchronized void delListSelectionListener(ListSelectionListener aListener) { - myListeners.remove(aListener); + listenerL.remove(aListener); + } + + /** + * Returns the backing {@link TableColumnHandler}. + */ + public TableColumnHandler getTableColumnHandler() + { + return workTableColumnHandler; } /** @@ -75,35 +116,17 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel myTable.getActionMap().put(aAction, aAction); } - @Override - public void actionPerformed(ActionEvent e) - { - Object source; - TableColumn selectedItem; - - // Determine the source - source = e.getSource(); - - if (source == searchTF || source == searchBox) - { - selectedItem = searchBox.getChosenItem(); - selectNextItem(selectedItem, searchTF.getText()); - } - } - /** * Returns the object located at the specified (view) row */ public synchronized G1 getItem(int aRow) { - G1 aObj; - aRow = sortTableModel.modelIndex(aRow); if (aRow == -1) return null; - aObj = myTableModel.getRowItem(aRow); - return aObj; + var retObj = viewTableModel.getRowItem(aRow); + return retObj; } /** @@ -111,13 +134,10 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel */ public synchronized G1 getSelectedItem() { - G1 selectedObj; - int selectedRow; - // Ensure the table is up to date updateTable(); - selectedRow = myTable.getSelectedRow(); + int selectedRow = myTable.getSelectedRow(); if (selectedRow == -1) return null; @@ -125,67 +145,40 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel if (selectedRow == -1) return null; - selectedObj = myTableModel.getRowItem(selectedRow); + G1 selectedObj = viewTableModel.getRowItem(selectedRow); return selectedObj; } /** - * getSelectedItems - Returns the list of selected items from the table + * Returns the list of selected items from the table */ public synchronized List getSelectedItems() { - List aList; - G1 selectedObj; - int[] selectedRows; - int selectedRow; - // Ensure the table is up to date updateTable(); - aList = Lists.newLinkedList(); - selectedRows = myTable.getSelectedRows(); - if (selectedRows != null) + // Transform from rows to items + var retItemL = new ArrayList(); + int[] idxArr = myTable.getSelectedRows(); + for (int aIdx : idxArr) { - for (int aInt : selectedRows) + int tmpIdx = sortTableModel.modelIndex(aIdx); + if (tmpIdx != -1) { - selectedRow = sortTableModel.modelIndex(aInt); - if (selectedRow != -1) - { - selectedObj = myTableModel.getRowItem(selectedRow); - aList.add(selectedObj); - } + G1 selectedObj = viewTableModel.getRowItem(tmpIdx); + retItemL.add(selectedObj); } } - return aList; - } - - @Override - public void itemChanged() - { - // Mark the table as being outdated - updateNeeded = true; - - // The advantage to the code below (as opposed to SwingUtilities.invokeLater() style) is - // that it is only updated when it absolutely is necessary. Thus if multiple updates come - // in before it is repainted they will be ignored. In the future this method may have an - // argument called isLazy which allow both styles of updating. - repaint(); + return retItemL; } /** - * This may be triggered indirectly via a network call after the method repaint() has been called. Do not call this - * method from a non gui thread + * Returns the associated SortTableModel */ - @Override - public void paint(Graphics g) + public TableSorter getSortTableModel() { - // Ensure the table is up to date - updateTable(); - - // Do the actual paint - if (g != null) - super.paint(g); + return sortTableModel; } /** @@ -195,8 +188,6 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel */ public synchronized void selectItem(G1 aObj) { - int chosenRow; - // Ensure we are executed only on the proper thread if (SwingUtilities.isEventDispatchThread() == false) throw new RuntimeException("ItemListPanel.selectItem() not executed on the AWT event dispatch thread."); @@ -214,7 +205,7 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel } else { - chosenRow = myTableModel.getRowIndex(aObj); + int chosenRow = viewTableModel.getRowIndex(aObj); if (chosenRow != -1) { chosenRow = sortTableModel.viewIndex(chosenRow); @@ -233,6 +224,23 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel myTable.repaint(); } + /** + * Ensures that the specified item is within the view. + */ + public synchronized void scrollToItem(G1 aItem) + { + // Transform from item to row index (view) + int tmpRow = viewTableModel.getRowIndex(aItem); + if (tmpRow == -1) + return; + + tmpRow = sortTableModel.viewIndex(tmpRow); + + // Ensure the row is in the view + if (JTableScrolling.isRowVisible(myTable, tmpRow) == false) + JTableScrolling.centerRow(myTable, tmpRow); + } + /** * Sets the table bodies color to aColor */ @@ -241,6 +249,22 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel tableScrollPane.getViewport().setBackground(aColor); } + /** + * Sets in the Comparator that will be used to sort the specified column. + */ + public void setSortComparator(int aColNum, Comparator aComparator) + { + sortTableModel.setColumnIndexComparator(aColNum, aComparator); + } + + /** + * Sets in the Comparator that will be used to sort items of type aType. + */ + public void setSortComparator(Class aType, Comparator aComparator) + { + sortTableModel.setColumnClassComparator(aType, aComparator); + } + /** * Sets whether the table can be sorted */ @@ -249,6 +273,54 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel sortTableModel.setSortingEnabled(aBool); } + @Override + public void actionPerformed(ActionEvent aEvent) + { + // Determine the source + var source = aEvent.getSource(); + if (source == searchTF || source == searchBox) + { + TableColumn selectedItem = searchBox.getChosenItem(); + selectNextItem(selectedItem, searchTF.getText()); + } + } + + @Override + public void handleItemEvent(Object aSource, ItemEventType aEventType) + { + // Time to update our selected items + if (aEventType == ItemEventType.ItemsSelected) + updateTableSelection(); + // Nothing to do just a repaint is needed + else if (aEventType == ItemEventType.ItemsMutated) + myTable.repaint(); + // Mark the table as being outdated + else if (aEventType == ItemEventType.ItemsChanged) + updateNeeded = true; + + // The advantage to the code below (as opposed to SwingUtilities.invokeLater() + // style) is that it is only updated when it absolutely is necessary. Thus if + // multiple updates come in before it is repainted they will be ignored. In + // the future this method may have an argument called isLazy which allow both + // styles of updating. + repaint(); + } + + /** + * This may be triggered indirectly via a network call after the method repaint() has been called. Do not call this + * method from a non gui thread + */ + @Override + public void paint(Graphics g) + { + // Ensure the table is up to date + updateTable(); + + // Do the actual paint + if (g != null) + super.paint(g); + } + @Override public void setEnabled(boolean aBool) { @@ -258,6 +330,20 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel @Override public void valueChanged(ListSelectionEvent aEvent) { + if (refItemProcessor instanceof ItemManager == false) + { + notifyListeners(aEvent); + return; + } + + // TODO: All ItemProcessor may go away and we interface with ItemManager + var tmpManager = (ItemManager) refItemProcessor; + + // Update the ItemManager's selection + tmpManager.delListener(this); + tmpManager.setSelectedItems(getSelectedItems()); + tmpManager.addListener(this); + notifyListeners(aEvent); } @@ -272,51 +358,50 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel /** * Forms the actual panel GUI */ - private void buildGuiArea(boolean hasSearchBox, boolean supportsMultipleSelection) + private void buildGuiArea(ItemHandler aItemHandler, QueryComposer aComposer, boolean aHasSearchBox, + boolean aSupportsMultipleSelection) { - JLabel tmpL; - - if (hasSearchBox == true) + if (aHasSearchBox == true) setLayout(new MigLayout("", "0[][][grow]0", "0[grow][]0")); else setLayout(new MigLayout("", "0[][][grow]0", "0[grow]0")); // Form the table - myTableModel = new ItemListTableModel(myItemHandler); - sortTableModel = new TableSorter(myTableModel); + workTableColumnHandler = new TableColumnHandler<>(aComposer.getItems()); + viewTableModel = new ItemListTableModel<>(aItemHandler, workTableColumnHandler); + sortTableModel = new TableSorter(viewTableModel); myTable = new JTable(sortTableModel); sortTableModel.setTableHeader(myTable.getTableHeader()); -//! sortTableModel.setSortingStatus(0, 1); myTable.setBackground(null); myTable.getSelectionModel().addListSelectionListener(this); - if (supportsMultipleSelection == false) + if (aSupportsMultipleSelection == false) myTable.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); else myTable.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); - // Notify the ItemHandler of its associated table and initialize the table - myItemHandler.initialize(myTable); + // Notify the TableModel of the associated table (and initialize the table) + viewTableModel.initialize(myTable); // Create the scroll pane and add the table to it. tableScrollPane = new JScrollPane(myTable); - add(tableScrollPane, "growx,growy,span 3"); + add(tableScrollPane, "growx,growy,span"); // The search section searchBox = null; searchTF = null; - if (hasSearchBox == true) + if (aHasSearchBox == true) { - tmpL = new JLabel("Find:"); - add(tmpL, "newline,span 1"); + var tmpL = new JLabel("Find:"); + add(tmpL, "newline"); searchBox = new GComboBox(this, new SearchBoxRenderer()); searchBox.setMaximumSize(searchBox.getPreferredSize()); - add(searchBox, "span 1"); + add(searchBox, ""); searchTF = new JTextField(""); searchTF.addActionListener(this); - add(searchTF, "growx,span 1"); + add(searchTF, "growx,span"); // Set in the preferred font tmpL.setFont(searchTF.getFont()); @@ -332,17 +417,16 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel */ protected void notifyListeners(ListSelectionEvent aEvent) { - List tmpList; - ListSelectionEvent tmpEvent; - - synchronized(this) + List tmpL; + synchronized (this) { - tmpList = Lists.newArrayList(myListeners); + tmpL = new ArrayList<>(listenerL); } // Notify our listeners - tmpEvent = new ListSelectionEvent(this, aEvent.getFirstIndex(), aEvent.getLastIndex(), aEvent.getValueIsAdjusting()); - for (ListSelectionListener aListener : tmpList) + var tmpEvent = new ListSelectionEvent(this, aEvent.getFirstIndex(), aEvent.getLastIndex(), + aEvent.getValueIsAdjusting()); + for (ListSelectionListener aListener : tmpL) aListener.valueChanged(tmpEvent); } @@ -351,47 +435,42 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel */ protected synchronized void rebuildItemList() { - Collection itemList; - Collection selectedObjSet; - int[] selectedRows; - int aRow; - // Insanity check - if (myItemProcessor == null) + if (refItemProcessor == null) return; // Get the old selected items - selectedObjSet = new LinkedList(); - selectedRows = myTable.getSelectedRows(); - if (selectedRows != null) + var selectedL = new ArrayList(); + int[] idxArr = myTable.getSelectedRows(); + for (int aInt : idxArr) { - for (int aInt : selectedRows) - { - aRow = sortTableModel.modelIndex(aInt); - selectedObjSet.add(myTableModel.getRowItem(aRow)); - } + int tmpIdx = sortTableModel.modelIndex(aInt); + selectedL.add(viewTableModel.getRowItem(tmpIdx)); } // Suspend listening to selection change events myTable.getSelectionModel().removeListSelectionListener(this); // Update our table with the new set of items - itemList = myItemProcessor.getItems(); - myTableModel.clear(); - myTableModel.addItems(itemList); + var itemL = refItemProcessor.getAllItems(); + viewTableModel.clear(); + viewTableModel.addItems(itemL); - // Reselect the old selected items - myTable.getSelectionModel().clearSelection(); - for (G1 aObj : selectedObjSet) + // Determine the row indexes to be selected + var tmpRowL = new ArrayList(); + for (G1 aObj : selectedL) { - aRow = myTableModel.getRowIndex(aObj); - if (aRow != -1) - { - aRow = sortTableModel.viewIndex(aRow); - myTable.getSelectionModel().addSelectionInterval(aRow, aRow); - } + int tmpRow = viewTableModel.getRowIndex(aObj); + if (tmpRow == -1) + continue; + + tmpRow = sortTableModel.viewIndex(tmpRow); + tmpRowL.add(tmpRow); } + // Select the appropriate rows + TableUtil.setSelection(myTable, null, tmpRowL); + // Restore listening to selection change events myTable.getSelectionModel().addListSelectionListener(this); } @@ -401,21 +480,27 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel */ protected synchronized void rebuildSearchBox() { - TableColumn selectedItem; - int numCols; - // Insanity check if (searchBox == null) return; // Save off the currently selected object - selectedItem = searchBox.getChosenItem(); + var selectedItem = searchBox.getChosenItem(); // Reconstitude searchBox - numCols = myTable.getColumnCount(); + var numCols = myTable.getColumnCount(); searchBox.removeAllItems(); for (int c1 = 0; c1 < numCols; c1++) - searchBox.addItem(myTable.getTableHeader().getColumnModel().getColumn(c1)); + { + TableColumn tmpTC = myTable.getTableHeader().getColumnModel().getColumn(c1); + + // Allow items to be searched if derived from JLabel + // TODO: In the future this should have a searchable flag rather + // this hack that checks if this is a child of JLabel + var tmpTCR = tmpTC.getCellRenderer(); + if (tmpTCR instanceof JLabel) + searchBox.addItem(tmpTC); + } // Set up the searchBox to appropriate state searchBox.removeActionListener(this); @@ -429,97 +514,88 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel /** * Utility to locate the next item to be selected, and change the section to that such item */ - protected synchronized void selectNextItem(TableColumn aTableColumn, String searchStr) + protected synchronized void selectNextItem(TableColumn aTableColumn, String aSearchStr) { - TableCellRenderer aRenderer; - JLabel tmpL; - Object aObj; - String currStr; - int colNum, chosenRow, startRow, numRows; - int cX; - boolean hardMatchRequired; - - if (aTableColumn == null || searchStr == null) + if (aTableColumn == null || aSearchStr == null) return; // Retrieve the model index and table renderer - aRenderer = aTableColumn.getCellRenderer(); - colNum = aTableColumn.getModelIndex(); + var aRenderer = aTableColumn.getCellRenderer(); + var colNum = aTableColumn.getModelIndex(); // Is a hard match required - hardMatchRequired = false; - if (searchStr.endsWith(" ") == true) + var hardMatchRequired = false; + if (aSearchStr.endsWith(" ") == true) { hardMatchRequired = true; - searchStr = searchStr.substring(0, searchStr.length() - 1); + aSearchStr = aSearchStr.substring(0, aSearchStr.length() - 1); } - startRow = myTable.getSelectedRow(); + var startRow = myTable.getSelectedRow(); if (startRow == -1) startRow = 0; else startRow++; - numRows = sortTableModel.getRowCount(); + var numRows = sortTableModel.getRowCount(); if (numRows == 0) return; // Search lower half of table - chosenRow = -1; - for (cX = startRow; cX < numRows; cX++) + var chosenRow = -1; + for (var cX = startRow; cX < numRows; cX++) { - aObj = sortTableModel.getValueAt(cX, colNum); - if (aObj != null) - { + var tmpObj = sortTableModel.getValueAt(cX, colNum); + if (tmpObj == null) + continue; - tmpL = (JLabel)aRenderer.getTableCellRendererComponent(myTable, aObj, false, false, cX, colNum); - currStr = tmpL.getText(); - if (currStr != null) + var tmpL = (JLabel) aRenderer.getTableCellRendererComponent(myTable, tmpObj, false, false, cX, colNum); + var currStr = tmpL.getText(); + if (currStr == null) + continue; + + if (hardMatchRequired == true) + { + if (currStr.equals(aSearchStr) == true) { - if (hardMatchRequired == true) - { - if (currStr.equals(searchStr) == true) - { - chosenRow = cX; - break; - } - } - else if (currStr.startsWith(searchStr) == true) - { - chosenRow = cX; - break; - } + chosenRow = cX; + break; } } + else if (currStr.contains(aSearchStr) == true) + { + chosenRow = cX; + break; + } } // Search upper half of table if (chosenRow == -1) { - for (cX = 0; cX < startRow; cX++) + for (var cX = 0; cX < startRow; cX++) { - aObj = sortTableModel.getValueAt(cX, colNum); - if (aObj != null) + var tmpObj = sortTableModel.getValueAt(cX, colNum); + if (tmpObj == null) + continue; + + var tmpL = (JLabel) aRenderer.getTableCellRendererComponent(myTable, tmpObj, false, false, cX, colNum); + var currStr = tmpL.getText(); + if (currStr == null) + continue; + + if (hardMatchRequired == true) { - tmpL = (JLabel)aRenderer.getTableCellRendererComponent(myTable, aObj, false, false, cX, colNum); - currStr = tmpL.getText(); - if (currStr != null) + if (currStr.equals(aSearchStr) == true) { - if (hardMatchRequired == true) - { - if (currStr.equals(searchStr) == true) - { - chosenRow = cX; - break; - } - } - else if (currStr.startsWith(searchStr) == true) - { - chosenRow = cX; - break; - } + chosenRow = cX; + break; } } + else if (currStr.contains(aSearchStr) == true) + { + chosenRow = cX; + break; + } } } @@ -542,8 +618,6 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel protected synchronized void updateTable() { // Bail if an update is no longer needed. - // If an update was originally scheduled, this will be false - // due to coalesion. if (updateNeeded == false) return; //System.out.println("ItemListPanel.updateTable() Addr:" + this.hashCode()); @@ -560,4 +634,18 @@ public class ItemListPanel extends JPanel implements ActionListener, ListSel updateNeeded = false; } + /** + * Helper method to update the table selection to match the state of the ItemManager. + *

    + * TODO: In the future this method may go away ItemProcessor and ItemManager are merged. + */ + private void updateTableSelection() + { + // Ensure the table is up to date + updateTable(); + + if (refItemProcessor instanceof ItemManager) + TableUtil.updateTableSelection(this, (ItemManager) refItemProcessor, myTable, sortTableModel); + } + } diff --git a/src/glum/gui/panel/itemList/ItemListTableModel.java b/src/glum/gui/panel/itemList/ItemListTableModel.java index 46a9aef..2a19ce6 100644 --- a/src/glum/gui/panel/itemList/ItemListTableModel.java +++ b/src/glum/gui/panel/itemList/ItemListTableModel.java @@ -1,78 +1,107 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JTable; import javax.swing.table.AbstractTableModel; -public class ItemListTableModel extends AbstractTableModel +/** + * TableModel that provides access to a collection of items handled by the provided {@link ItemHandler}. + * + * @param + * @param + * + * @author lopeznr1 + */ +public class ItemListTableModel> extends AbstractTableModel { - private ItemHandler myHandler; - private ArrayList myVector; + // Ref vars + private final ItemHandler refItemHandler; + + // State vars + private final TableColumnHandler workTableColumnHandler; + private final ArrayList myItemL; /** - * Constructor + * Standard Constructor + * + * @param aItemHandler + * @param aTableColumnHandler */ - public ItemListTableModel(ItemHandler aHandler) + public ItemListTableModel(ItemHandler aItemHandler, TableColumnHandler aTableColumnHandler) { - myHandler = aHandler; + refItemHandler = aItemHandler; + workTableColumnHandler = aTableColumnHandler; - myVector = new ArrayList(); + myItemL = new ArrayList<>(); } @Override public int getColumnCount() { - if (myHandler == null) - return 0; - - return myHandler.getColumnCount(); + return workTableColumnHandler.getColumnCount(); } @Override public int getRowCount() { - return myVector.size(); + return myItemL.size(); } @Override - public String getColumnName(int col) + public String getColumnName(int aCol) { - if (myHandler == null) + return workTableColumnHandler.getColumnLabel(aCol); + } + + @Override + public Object getValueAt(int aRow, int aCol) + { + // Locate the lookup corresponding to the specified column + var tmpEnum = workTableColumnHandler.getEnum(aCol); + if (tmpEnum == null) return null; - return myHandler.getColumnLabel(col); + // Retrieve the appropriate data field of the appropriate item + return refItemHandler.getValue(myItemL.get(aRow), tmpEnum); } @Override - public Object getValueAt(int row, int col) + public Class getColumnClass(int aCol) { - if (myHandler == null) - return null; - - return myHandler.getColumnValue(myVector.get(row), col); + return workTableColumnHandler.getColumnClass(aCol); } @Override - public Class getColumnClass(int col) + public boolean isCellEditable(int aRow, int aCol) { - return myHandler.getColumnClass(col); + return workTableColumnHandler.isColumnEditable(aCol); } @Override - public boolean isCellEditable(int row, int col) + public void setValueAt(Object aValue, int aRow, int aCol) { - if (myHandler == null) - return false; - - return myHandler.isCellEditable(col); - } - - @Override - public void setValueAt(Object value, int row, int col) - { - if (myHandler == null) + // Locate the lookup corresponding to the specified column + var tmpEnum = workTableColumnHandler.getEnum(aCol); + if (tmpEnum == null) return; - myHandler.setColumnValue(myVector.get(row), col, value); + // Update the appropriate data field of the appropriate item + refItemHandler.setValue(myItemL.get(aRow), tmpEnum, aValue); } /** @@ -80,15 +109,13 @@ public class ItemListTableModel extends AbstractTableModel */ public void clear() { - int endIndex; - - if (myVector.isEmpty() == true) + if (myItemL.isEmpty() == true) return; - endIndex = myVector.size() - 1; - myVector.clear(); + int endIdx = myItemL.size() - 1; + myItemL.clear(); - fireTableRowsDeleted(0, endIndex); + fireTableRowsDeleted(0, endIdx); } /** @@ -96,18 +123,16 @@ public class ItemListTableModel extends AbstractTableModel */ public int getRowIndex(G1 aItem) { - int aIndex; - if (aItem == null) return -1; - aIndex = 0; - for (G1 aObj : myVector) + int tmpIdx = 0; + for (G1 aObj : myItemL) { if (aObj.equals(aItem) == true) - return aIndex; + return tmpIdx; - aIndex++; + tmpIdx++; } return -1; @@ -116,33 +141,40 @@ public class ItemListTableModel extends AbstractTableModel /** * Returns the item associated with the row */ - public G1 getRowItem(int row) + public G1 getRowItem(int aRow) { - if (row < 0 || row >= myVector.size()) + if (aRow < 0 || aRow >= myItemL.size()) return null; - return myVector.get(row); + return myItemL.get(aRow); } /** * Adds the collection to our TableModel */ - public void addItems(Collection aCollection) + public void addItems(Collection aItemC) { - int startIndex, endIndex; - - if (aCollection == null) + if (aItemC == null) return; - if (aCollection.isEmpty() == true) + if (aItemC.isEmpty() == true) return; - startIndex = myVector.size(); - endIndex = startIndex + aCollection.size(); + var startIndex = myItemL.size(); + var endIndex = startIndex + aItemC.size(); - myVector.addAll(aCollection); + myItemL.addAll(aItemC); fireTableRowsInserted(startIndex, endIndex); } + /** + * Notifies this {@link TableColumnHandler} of the associated {@link JTable}. + */ + public void initialize(JTable aTable) + { + // Delegate + workTableColumnHandler.initialize(aTable); + } + } diff --git a/src/glum/gui/panel/itemList/ItemProcessor.java b/src/glum/gui/panel/itemList/ItemProcessor.java index 6877771..03e7f52 100644 --- a/src/glum/gui/panel/itemList/ItemProcessor.java +++ b/src/glum/gui/panel/itemList/ItemProcessor.java @@ -1,26 +1,48 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.util.*; +import com.google.common.collect.ImmutableList; +import glum.item.ItemEventListener; + +/** + * Interface that defines methods for accessing a collection of items. Support is provided to allow notification of when + * the items change via the ItemEventListener mechanism. + * + * @author lopeznr1 + */ public interface ItemProcessor { + /** + * Adds a {@link ItemEventListener} to this ItemProcessor. + */ + public void addListener(ItemEventListener aListener); + + /** + * Removes a {@link ItemEventListener} from this ItemProcessor. + */ + public void delListener(ItemEventListener aListener); + + /** + * Returns a list of all items in this ItemProcessor + */ + public ImmutableList getAllItems(); + /** * Returns the number of items in this ItemProcessor */ public int getNumItems(); - /** - * Returns a list of all items in this ItemProcessor - */ - public Collection getItems(); - - /** - * Registers for notification when items are added/removed from this ItemProcessor. - */ - public void addItemChangeListener(ItemChangeListener aItemChangeListener); - - /** - * Deregisters for notification of item list change events. - */ - public void removeItemChangeListener(ItemChangeListener aItemChangeListener); } diff --git a/src/glum/gui/panel/itemList/RegistryProcessor.java b/src/glum/gui/panel/itemList/RegistryProcessor.java index 673c9a1..006783c 100644 --- a/src/glum/gui/panel/itemList/RegistryProcessor.java +++ b/src/glum/gui/panel/itemList/RegistryProcessor.java @@ -1,52 +1,74 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.util.*; +import java.util.List; -import glum.registry.*; +import com.google.common.collect.ImmutableList; +import glum.registry.Registry; +import glum.registry.ResourceListener; + +/** + * Implementation of {@link ItemProcessor} backed by the specified Registry and resource key. + *

    + * Notification will be sent out whenever the registry changes. + * + * @author lopeznr1 + */ public class RegistryProcessor extends BasicItemProcessor implements ResourceListener { - private Registry refRegistry; - private Class resourceClass; - private Object resourceKey; + // Ref vars + private final Registry refRegistry; + private final Class resourceClass; + private final Object resourceKey; - private Collection itemList; + // State vars + private ImmutableList itemL; - /** - * Constructor - */ + /** Standard Constructor */ public RegistryProcessor(Registry aRegistry, Object aResourceKey, Class aResourceClass) { - super(); - refRegistry = aRegistry; resourceClass = aResourceClass; resourceKey = aResourceKey; // Initialize our state vars - itemList = new ArrayList(); + itemL = ImmutableList.of(); // Register for events of interest refRegistry.addResourceListener(resourceKey, this); } @Override - public synchronized Collection getItems() + public ImmutableList getAllItems() { - return new ArrayList(itemList); + return itemL; } @Override public synchronized int getNumItems() { - return itemList.size(); + return itemL.size(); } @Override public void resourceChanged(Registry aRegistry, Object aKey) { // Retrieve the list of tiles - itemList = refRegistry.getResourceItems(resourceKey, resourceClass); + List tmpL = refRegistry.getResourceItems(resourceKey, resourceClass); + itemL = ImmutableList.copyOf(tmpL); // Notify our listeners notifyListeners(); diff --git a/src/glum/gui/panel/itemList/SearchBoxRenderer.java b/src/glum/gui/panel/itemList/SearchBoxRenderer.java index 9334c7d..d369a03 100644 --- a/src/glum/gui/panel/itemList/SearchBoxRenderer.java +++ b/src/glum/gui/panel/itemList/SearchBoxRenderer.java @@ -1,33 +1,43 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; -import java.awt.*; +import java.awt.Component; + import javax.swing.*; -import javax.swing.table.*; +import javax.swing.table.TableColumn; public class SearchBoxRenderer extends DefaultListCellRenderer { - /** - * Constructor - */ + /*** Standard Constructor */ public SearchBoxRenderer() { - super(); + ; // Nothing to do } @Override - public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, boolean hasFocus) + public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, + boolean hasFocus) { - JLabel retL; - String aStr; - - retL = (JLabel)super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); + var retL = (JLabel) super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); if (aObj instanceof TableColumn) { - aStr = "" + ((TableColumn)aObj).getHeaderValue(); - if (aStr.equals("null") == true || aStr.equals("") == true) - aStr = "" + ((TableColumn)aObj).getIdentifier(); + var tmpStr = "" + ((TableColumn) aObj).getHeaderValue(); + if (tmpStr.equals("null") == true || tmpStr.equals("") == true) + tmpStr = "" + ((TableColumn) aObj).getIdentifier(); - retL.setText(aStr); + retL.setText(tmpStr); } return retL; diff --git a/src/glum/gui/panel/itemList/StaticItemProcessor.java b/src/glum/gui/panel/itemList/StaticItemProcessor.java index 141025c..8f19204 100644 --- a/src/glum/gui/panel/itemList/StaticItemProcessor.java +++ b/src/glum/gui/panel/itemList/StaticItemProcessor.java @@ -1,23 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList; import java.util.*; +import com.google.common.collect.ImmutableList; + +/** + * Implementation of {@link ItemProcessor} which maintains a static list of items. + * + * @author lopeznr1 + */ public class StaticItemProcessor extends BasicItemProcessor { - private ArrayList itemList; + private ImmutableList itemL; - public StaticItemProcessor() + /** + * Standard Constructor + */ + public StaticItemProcessor(List aItemL) { - super(); - - itemList = new ArrayList(); + itemL = ImmutableList.copyOf(aItemL); } - public StaticItemProcessor(List aList) + /** + * Empty Constructor + */ + public StaticItemProcessor() { - super(); - - itemList = new ArrayList(aList); + this(ImmutableList.of()); } /** @@ -27,30 +49,30 @@ public class StaticItemProcessor extends BasicItemProcessor */ public int indexOf(G1 aItem) { - return itemList.indexOf(aItem); + return itemL.indexOf(aItem); } /** * Replaces the static list of items stored with aItemList */ - public void setItems(Collection aItemList) + public void setItems(Collection aItemC) { - itemList = new ArrayList(aItemList); + itemL = ImmutableList.copyOf(aItemC); // Notify our listeners notifyListeners(); } @Override - public synchronized ArrayList getItems() + public synchronized ImmutableList getAllItems() { - return new ArrayList(itemList); + return itemL; } @Override public synchronized int getNumItems() { - return itemList.size(); + return itemL.size(); } } diff --git a/src/glum/gui/panel/itemList/TableColumnHandler.java b/src/glum/gui/panel/itemList/TableColumnHandler.java new file mode 100644 index 0000000..52cf882 --- /dev/null +++ b/src/glum/gui/panel/itemList/TableColumnHandler.java @@ -0,0 +1,521 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.panel.itemList; + +import java.util.*; + +import javax.swing.JTable; + +import com.google.common.collect.ImmutableList; + +import glum.gui.panel.itemList.query.QueryAttribute; +import glum.gui.panel.itemList.query.QueryTableCellRenderer; +import glum.gui.table.SortDir; +import glum.gui.table.TableSorter; +import glum.unit.*; + +/** + * Support class for {@link ItemListTableModel}. + * + * @author lopeznr1 + */ +public class TableColumnHandler> implements UnitListener +{ + // Attributes + private final ArrayList> fullAttributeL; + + // State vars + private JTable myOwner; + private ArrayList> orderAttributeL; + + /** Standard Constructor */ + public TableColumnHandler(Collection> aQueryAttrC) + { + myOwner = null; + + fullAttributeL = new ArrayList<>(); + orderAttributeL = new ArrayList<>(); + + int evalIndex = 0; + for (QueryAttribute aAttr : aQueryAttrC) + { + // Ensure the model index is appropriately initialized + if (evalIndex != aAttr.modelIndex) + throw new RuntimeException( + "Improper initialization. Expected Index: " + evalIndex + " Received Index: " + aAttr.modelIndex); + + fullAttributeL.add(aAttr); + orderAttributeL.add(aAttr); + evalIndex++; + + // Register for the appropriate unit events + aAttr.refUnitProvider.addListener(this); + } + + fullAttributeL.trimToSize(); + orderAttributeL.trimToSize(); + } + + /** + * Returns the (enum) lookup associated with the specified column index. + *

    + * Returns null if the column index is out of range. + */ + public G2 getEnum(int aColIdx) + { + // Insanity check + if (aColIdx < 0 && aColIdx >= fullAttributeL.size()) + return null; + + return fullAttributeL.get(aColIdx).refKey; + } + + /** + * Notifies the {@link TableColumnHandler} of its associated JTable. This table should be updated or painted whenever + * the internals of this {@link TableColumnHandler} change. + */ + public void initialize(JTable aOwner) + { + // This method is only allowed to be called once! + if (myOwner != null) + throw new RuntimeException("TableColumnHandler already initialized!"); + + myOwner = aOwner; + + var tmpTableHeader = myOwner.getTableHeader(); + var tmpTableColumnModel = tmpTableHeader.getColumnModel(); + + // Customize overall settings + tmpTableHeader.setReorderingAllowed(false); + + // Grab all of the precomputed columns from the table + // and store with their associated queryAttributes + for (int c1 = 0; c1 < fullAttributeL.size(); c1++) + { + var tmpTableColumn = tmpTableColumnModel.getColumn(c1); + fullAttributeL.get(c1).assocTableColumn = tmpTableColumn; + } + + // Rebuild the table columns; Needed so that only the + // visible ones are displayed. Do this only after you + // have grabbed the table columns as aTableColumnModel + // will strictly contain the "visible" ones + rebuildTableColumns(); + } + + /** + * Returns the number of columns associated with the tabular data. + */ + public int getColumnCount() + { + return fullAttributeL.size(); + } + + /** + * Returns the class of data type for each column in the tabular data. + */ + public Class getColumnClass(int colNum) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return String.class; + + return fullAttributeL.get(colNum).refClass; + } + + public int getColumnDefaultWidth(int colNum) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return -1; + + // Get the default and min size + var defaultSize = fullAttributeL.get(colNum).defaultSize; + var minSize = fullAttributeL.get(colNum).minSize; + + // Ensure size makes sense + if (defaultSize < minSize) + return minSize; + + return defaultSize; + } + + /** + * getColumnMinWidth + */ +// public int getColumnMinWidth(int colNum) +// { +// // Insanity check +// if (queryAttributes == null) +// return -1; +// +// if (colNum < 0 && colNum >= queryAttributes.length) +// return -1; +// +// return queryAttributes[colNum].minSize; +// } + + /** + * getColumnMaxWidth + */ + public int getColumnMaxWidth(int colNum) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return -1; + + // Get the default and max size + var defaultSize = fullAttributeL.get(colNum).defaultSize; + var maxSize = fullAttributeL.get(colNum).maxSize; + + // Ensure size makes sense + if (defaultSize > maxSize && maxSize != -1) + return defaultSize; + + return maxSize; + } + + /** + * Returns the text label that should be used for the title of the column. + */ + public String getColumnLabel(int colNum) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return ""; + + var tmpAttribute = fullAttributeL.get(colNum); + + // Retrieve the associated unit + var tmpUnit = tmpAttribute.refUnitProvider.getUnit(); + + // Retrieve the base column label + var tmpStr = tmpAttribute.label; + + // Append the unit name to the column label + if (tmpUnit != null && "".equals(tmpUnit.getLabel(false)) == false) + return tmpStr + " [" + tmpUnit.getLabel(false) + "]"; + else + return tmpStr; + } + + /** + * Returns an ordered list of the {@link QueryAttribute}s. + */ + public ArrayList> getOrderedAttributes() + { + if (myOwner.getModel() instanceof TableSorter aTableSorter) + { + for (var aAttr : orderAttributeL) + { + var tmpIdx = aAttr.modelIndex; + aAttr.sortDir = aTableSorter.getSortDir(tmpIdx); + } + } + + return new ArrayList>(orderAttributeL); + } + + /** + * Returns a list of model indexes which define the priority of the sorted columns. + */ + public List getSortPriorityList() + { + var tmpModel = myOwner.getModel(); + if (tmpModel instanceof TableSorter aTableSorter) + { + var retSortPriorityL = new ArrayList(); + var tmpSortStateM = aTableSorter.getSortState(); + for (var aModelIdx : tmpSortStateM.keySet()) + retSortPriorityL.add(aModelIdx); + + return retSortPriorityL; + } + + return ImmutableList.of(); + } + + /** + * Returns true if the data associated at the specified column can be edited. + */ + public boolean isColumnEditable(int colNum) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return false; + + if (fullAttributeL.get(colNum).editor == null) + return false; + + return true; + } + + @Override + public void unitChanged(UnitProvider aManager, String aKey) + { + update(); + + myOwner.repaint(); + + var tmpTableHeader = myOwner.getTableHeader(); + if (tmpTableHeader != null) + tmpTableHeader.repaint(); + +// for (QueryAttribute aAttribute : queryAttributes) +// { +// if (aKey.equals(aAttribute.unitKey) == true) +// } +// +// Tile aTile; +// +// // Update our listPanel to by sync with the active tile +// if (aKey.equals("tile.active") == true) +// { +// aTile = refRegistry.getSingleton(aKey, Tile.class); +// +// listPanel.removeListSelectionListener(this); +// listPanel.selectItem(aTile); +// listPanel.addListSelectionListener(this); +// } +// +// updateGui(); + } + + public Unit getUnit(int aColNum) + { + // Insanity check + if (aColNum < 0 && aColNum >= fullAttributeL.size()) + return null; + + var tmpAttribute = fullAttributeL.get(aColNum); + return tmpAttribute.refUnitProvider.getUnit(); + } + + public void setColumnAlignment(int colNum, int aAlignment) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return; + + fullAttributeL.get(colNum).alignment = aAlignment; + } + + public void setColumnLabel(int colNum, String aLabel) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return; + + fullAttributeL.get(colNum).label = aLabel; + } + + public void setColumnPosition(int colNum, int aPosition) + { + // Insanity check + if (colNum < 0 || aPosition < 0 || colNum >= fullAttributeL.size() || aPosition >= fullAttributeL.size()) + return; + + orderAttributeL.remove(fullAttributeL.get(colNum)); + orderAttributeL.add(aPosition, fullAttributeL.get(colNum)); + } + + public void setColumnSize(int colNum, int aSize) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return; + + System.out.println("[QueryItemHandler.java] Changing size of colNum: " + aSize); + fullAttributeL.get(colNum).defaultSize = aSize; + } + + public void setColumnSortDir(int colNum, SortDir aSortDir) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return; + + fullAttributeL.get(colNum).sortDir = aSortDir; + } + + public void setColumnVisible(int colNum, boolean isVisible) + { + // Insanity check + if (colNum < 0 && colNum >= fullAttributeL.size()) + return; + + fullAttributeL.get(colNum).isVisible = isVisible; + + // Update our table + if (myOwner != null) + rebuildTableColumns(); + } + + /** + * moveSortedAttribute + */ + public void moveSortedAttribute(int currIndex, int newIndex) + { + var tmpItem = orderAttributeL.get(currIndex); + orderAttributeL.remove(currIndex); + orderAttributeL.add(newIndex, tmpItem); + } + + /** + * Method to reconfigure the columns of the table. This will update the display order of the columns as well as the + * individual relevant attributes of the column. Currently supported relevant attributes are those defined in the + * method {@link QueryAttribute#setConfig}. + * + * Note any non specified columns will appear last according to the previous order. + * + * @param orderSet: + * Ordered set of QueryAttributes with matching modelIndexes + */ + public void setOrderAndConfig(Collection> aOrderArr) + { + // Form a lookup map (modelIndex to attribute) + var tmpItemM = new LinkedHashMap>(); + for (var aItem : orderAttributeL) + tmpItemM.put(aItem.modelIndex, aItem); + + // Rebuild the sortedQueryAttribute list to conform with + // - the specified order of orderList + // - synch up relevant attributes + orderAttributeL.clear(); + for (QueryAttribute aItem : aOrderArr) + { + var workItem = tmpItemM.remove(aItem.modelIndex); + if (workItem != null) + { + workItem.setConfig(aItem); + orderAttributeL.add(workItem); + } + } + + orderAttributeL.addAll(tmpItemM.values()); + tmpItemM.clear(); + } + + /** + * Returns a list of model indexes which define the priority of the sorted columns. + */ + public void setSortPriorityList(List aSortPriorityL) + { + var tmpModel = myOwner.getModel(); + if (tmpModel instanceof TableSorter aTableSorter) + { + var tmpAttributeM = new HashMap>(); + for (var aAttribute: fullAttributeL) + tmpAttributeM.put(aAttribute.modelIndex, aAttribute); + + var tmpSortStateM = new LinkedHashMap(); + for (var aModelIdx : aSortPriorityL) + { + var tmpAttribute = tmpAttributeM.get(aModelIdx); + if (tmpAttribute == null) + continue; + + tmpSortStateM.put(aModelIdx, tmpAttribute.sortDir); + } + + aTableSorter.setSortState(tmpSortStateM); + } + } + + /** + * Helper method to initialize a TableColumn with the actual QueryAttribute properties. + * + * @param colNum: + * Index into column model + */ + protected void initializeTableColumn(int aColNum) + { + // Insanity check + if (aColNum < 0 && aColNum >= fullAttributeL.size()) + return; + + // Get the associated table column + var tmpAttribute = fullAttributeL.get(aColNum); + var tmpTableColumn = tmpAttribute.assocTableColumn; + if (tmpTableColumn == null) + return; + + // Retrieve settings of interest + var label = getColumnLabel(aColNum); + var defaultWidth = getColumnDefaultWidth(aColNum); + var maxWidth = getColumnMaxWidth(aColNum); + var minWidth = tmpAttribute.minSize; + + // Set up the column's renderer + var tmpRenderer = tmpAttribute.renderer; + if (tmpRenderer == null) + tmpRenderer = new QueryTableCellRenderer(tmpAttribute); + tmpTableColumn.setCellRenderer(tmpRenderer); + + // Set up the column's editor + tmpTableColumn.setCellEditor(tmpAttribute.editor); + + // Set up the column's size attributes + tmpTableColumn.setMinWidth(minWidth); + tmpTableColumn.setMaxWidth(maxWidth); + tmpTableColumn.setPreferredWidth(defaultWidth); + tmpTableColumn.setWidth(defaultWidth); + + // Set up the column header + tmpTableColumn.setHeaderValue(label); + } + +// TODO -> This should probably be protected + public void rebuildTableColumns() + { + // Enforce the constraints (QueryAttribute) on the associated columns + for (int c1 = 0; c1 < fullAttributeL.size(); c1++) + initializeTableColumn(c1); + + // Remove all of the columns from the table + for (var aAttribute : fullAttributeL) + myOwner.removeColumn(aAttribute.assocTableColumn); + + // Add in only the columns that are visible + for (var aAttribute : orderAttributeL) + { + if (aAttribute.isVisible == false) + continue; + myOwner.addColumn(aAttribute.assocTableColumn); + } + } + + /** + * Helper method that updates all the TableColumn renders with the appropriate unit. + */ + private void update() + { + for (var aAttribute : fullAttributeL) + { + if (aAttribute.assocTableColumn != null) + { + var tmpTableColumn = aAttribute.assocTableColumn; + var tmpObject = tmpTableColumn.getCellRenderer(); + if (tmpObject instanceof QueryTableCellRenderer aRenderer) + { + aAttribute.assocTableColumn.setHeaderValue(getColumnLabel(aAttribute.modelIndex)); + + var tmpUnit = aAttribute.refUnitProvider.getUnit(); + aRenderer.setUnit(tmpUnit); + } + } + } + } + +} diff --git a/src/glum/gui/panel/itemList/config/AddProfilePanel.java b/src/glum/gui/panel/itemList/config/AddProfilePanel.java index 3063e86..331bd4e 100644 --- a/src/glum/gui/panel/itemList/config/AddProfilePanel.java +++ b/src/glum/gui/panel/itemList/config/AddProfilePanel.java @@ -1,16 +1,26 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; -import glum.gui.panel.generic.TextInputPanel; +import glum.gui.panel.generic.BaseTextInputPanel; import java.awt.*; import java.util.*; import com.google.common.collect.Sets; -public class AddProfilePanel extends TextInputPanel +public class AddProfilePanel extends BaseTextInputPanel { - // Constants - public static final String DEFAULT_NAME = "Default"; - // State vars protected Set reservedSet; @@ -51,7 +61,7 @@ public class AddProfilePanel extends TextInputPanel { infoMsg = "Please enter a valid profile name."; } - else if (inputStr.equals(DEFAULT_NAME) == true) + else if (inputStr.equals(ProfileConfig.DEFAULT_NAME) == true) { infoMsg = "Name is reserved. Please pick another."; } diff --git a/src/glum/gui/panel/itemList/config/ConfigHandler.java b/src/glum/gui/panel/itemList/config/ConfigHandler.java index 2521c06..6a8957b 100644 --- a/src/glum/gui/panel/itemList/config/ConfigHandler.java +++ b/src/glum/gui/panel/itemList/config/ConfigHandler.java @@ -1,77 +1,60 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; -import glum.gui.panel.itemList.BasicItemHandler; +import glum.gui.panel.itemList.ItemHandler; +import glum.gui.panel.itemList.ItemListPanel; import glum.gui.panel.itemList.query.QueryAttribute; -import glum.gui.panel.itemList.query.QueryComposer; -public class ConfigHandler extends BasicItemHandler +/** + * Implementation of {@link ItemHandler} that allows for tabular access of columns shown in a {@link ItemListPanel}. + * + * @author lopeznr1 + */ +public class ConfigHandler> implements ItemHandler, ConfigLookUp> { - public ConfigHandler(QueryComposer aComposer) - { - super(aComposer); - } - @Override - public Object getColumnValue(QueryAttribute aObj, int colNum) + public Object getValue(QueryAttribute aItem, ConfigLookUp aEnum) { - Enum refKey; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return null; - - refKey = fullAttributeList.get(colNum).refKey; - return getColumnValue(aObj, refKey); - } - - @Override - public void setColumnValue(QueryAttribute aObj, int colNum, Object aValue) - { - Enum refKey; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - - refKey = fullAttributeList.get(colNum).refKey; - setColumnValue(aObj, refKey, aValue); - } - - /** - * Method to get the value from aObj described by aRefKey - */ - public Object getColumnValue(QueryAttribute aItem, Enum aRefKey) - { - switch ((ConfigLookUp)aRefKey) + switch (aEnum) { case IsVisible: - return aItem.isVisible; + return aItem.isVisible; case Name: - return aItem.refKey; + return aItem.refKey; case Label: - return aItem.label; + return aItem.label; default: - break; + break; } - return null; + throw new RuntimeException("Unsupported enum:" + aEnum); } - /** - * Method to get the value from aObj described by aRefKey - */ - public void setColumnValue(QueryAttribute aItem, Enum aRefKey, Object aValue) + @Override + public void setValue(QueryAttribute aItem, ConfigLookUp aEnum, Object aValue) { - if (aRefKey == ConfigLookUp.IsVisible) + if (aEnum == ConfigLookUp.IsVisible) { - aItem.isVisible = (Boolean)aValue; + aItem.isVisible = (Boolean) aValue; return; } - throw new RuntimeException("Unsupported Operation."); + throw new RuntimeException("Unsupported enum:" + aEnum); } } diff --git a/src/glum/gui/panel/itemList/config/ConfigLookUp.java b/src/glum/gui/panel/itemList/config/ConfigLookUp.java index 05ffd72..e049064 100644 --- a/src/glum/gui/panel/itemList/config/ConfigLookUp.java +++ b/src/glum/gui/panel/itemList/config/ConfigLookUp.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; public enum ConfigLookUp diff --git a/src/glum/gui/panel/itemList/config/EditTablePanel.java b/src/glum/gui/panel/itemList/config/EditTablePanel.java index c8a21b5..27e8e5d 100644 --- a/src/glum/gui/panel/itemList/config/EditTablePanel.java +++ b/src/glum/gui/panel/itemList/config/EditTablePanel.java @@ -1,116 +1,129 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; -import java.awt.*; +import java.awt.Component; +import java.awt.Dimension; import java.awt.event.*; import java.io.IOException; -import java.util.*; +import java.util.HashSet; +import java.util.List; import javax.swing.*; -import javax.swing.border.*; +import javax.swing.border.EmptyBorder; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableCellRenderer; -import com.google.common.collect.Sets; - -import glum.gui.*; -import glum.gui.action.*; +import glum.gui.FocusUtil; +import glum.gui.GuiUtil; +import glum.gui.action.ClickAction; import glum.gui.component.GComboBox; import glum.gui.component.GTextField; -import glum.gui.icon.ArrowNorthIcon; -import glum.gui.icon.ArrowSouthIcon; -import glum.gui.icon.DeleteIcon; -import glum.gui.misc.*; -import glum.gui.panel.*; -import glum.gui.panel.itemList.BasicItemHandler; -import glum.gui.panel.itemList.ItemHandler; -import glum.gui.panel.itemList.ItemListPanel; -import glum.gui.panel.itemList.StaticItemProcessor; -import glum.gui.panel.itemList.query.*; +import glum.gui.icon.*; +import glum.gui.misc.BooleanCellEditor; +import glum.gui.misc.BooleanCellRenderer; +import glum.gui.panel.GlassPanel; +import glum.gui.panel.generic.MessagePanel; +import glum.gui.panel.itemList.*; +import glum.gui.panel.itemList.query.QueryAttribute; +import glum.gui.panel.itemList.query.QueryComposer; import glum.zio.*; import net.miginfocom.swing.MigLayout; -public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj, ListSelectionListener +/** + * UI component that allows for the configuration of the table contained by an {@link ItemListPanel}. + *

    + * Construction of this panel involves passing a reference to the {@link ItemListPanel}'s {@link TableColumnHandler}. + * This handler is retrieved via {@link ItemListPanel#getTableColumnHandler()}. + * + * @author lopeznr1 + */ +public class EditTablePanel> extends GlassPanel + implements ActionListener, ItemListener, ListSelectionListener, ZioObj { + // Attributes + private final TableColumnHandler refTableColumnHandler; + // GUI vars - protected JLabel titleL; - protected JRadioButton profileRB, customRB; - protected GComboBox profileBox; - protected ItemListPanel listPanel; - protected BooleanCellEditor col0Editor; - protected BooleanCellRenderer col0Renderer; - protected DefaultTableCellRenderer col1Renderer; - protected JButton closeB, saveB, upB, downB, deleteB; - protected JLabel labelL; - protected GTextField labelTF; - protected Font smallFont; - protected AddProfilePanel profilePanel; + private JLabel titleL; + private JRadioButton profileRB, customRB; + private GComboBox> profileBox; + private ItemListPanel, ConfigLookUp> listPanel; + private BooleanCellEditor col0Editor; + private BooleanCellRenderer col0Renderer; + private DefaultTableCellRenderer col1Renderer; + private JButton closeB, saveB, upB, downB, deleteB; + private JLabel labelL; + private GTextField labelTF; + private AddProfilePanel profilePanel; // State vars - protected BasicItemHandler refItemHandler; - protected StaticItemProcessor myItemProcessor; + protected StaticItemProcessor> myItemProcessor; - public EditTablePanel(Component aParent, BasicItemHandler aItemHandler) + /** Standard Constructor */ + public EditTablePanel(Component aParent, TableColumnHandler aTableColumnHandler) { super(aParent); // State vars - refItemHandler = aItemHandler; + refTableColumnHandler = aTableColumnHandler; // Build the actual GUI - smallFont = (new JTextField()).getFont(); buildGuiArea(); setPreferredSize(new Dimension(250, getPreferredSize().height)); - + profilePanel = new AddProfilePanel(this); profilePanel.setSize(375, 140); profilePanel.addActionListener(this); - + syncGui(); updateGui(); - + // Set up some keyboard shortcuts FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(closeB)); } /** - * Adds a predefined profile (as specified in aConfig) to the - * list of available profiles. + * Adds a predefined profile (as specified in aConfig) to the list of available profiles. + * * @param aConfig */ - public void addConfig(ProfileConfig aConfig) + public void addConfig(ProfileConfig aConfig) { profileBox.addItem(aConfig); } - - public ArrayList getAllConfig() + + public List> getAllConfig() { return profileBox.getAllItems(); } - - public BasicItemHandler getItemHandler() - { - return refItemHandler; - } - + /** - * Synchronizes the gui to match the model + * Synchronizes the gui to match the model */ public void syncGui() { - myItemProcessor.setItems(refItemHandler.getSortedAttributes()); + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); } - + @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - QueryAttribute aItem; - int index; + var tmpItem = listPanel.getSelectedItem(); - aItem = listPanel.getSelectedItem(); - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == labelTF) { updateLayerAttribute(); @@ -118,8 +131,7 @@ public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj } else if (source == deleteB) { - profileBox.removeItem(profileBox.getChosenItem()); - actionPerformed(new ActionEvent(profileBox, ID_UPDATE, null)); + doActionDel(); } else if (source == closeB) { @@ -128,145 +140,157 @@ public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj } else if (source == saveB) { - Set nameSet; - - nameSet = Sets.newHashSet(); - for (ProfileConfig aProfile : profileBox.getAllItems()) - nameSet.add(aProfile.getName()); - + var nameS = new HashSet(); + for (ProfileConfig aProfile : profileBox.getAllItems()) + nameS.add(aProfile.name()); + profilePanel.resetGui(); - profilePanel.setReservedNames(nameSet); + profilePanel.setReservedNames(nameS); profilePanel.setVisible(true); } else if (source == upB) { - index = myItemProcessor.indexOf(aItem); - refItemHandler.moveSortedAttribute(index, index - 1); - refItemHandler.rebuildTableColumns(); - - myItemProcessor.setItems(refItemHandler.getSortedAttributes()); + var index = myItemProcessor.indexOf(tmpItem); + refTableColumnHandler.moveSortedAttribute(index, index - 1); + refTableColumnHandler.rebuildTableColumns(); + + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); } else if (source == downB) { - index = myItemProcessor.indexOf(aItem); - refItemHandler.moveSortedAttribute(index, index + 1); - refItemHandler.rebuildTableColumns(); - - myItemProcessor.setItems(refItemHandler.getSortedAttributes()); + var index = myItemProcessor.indexOf(tmpItem); + refTableColumnHandler.moveSortedAttribute(index, index + 1); + refTableColumnHandler.rebuildTableColumns(); + + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); } else if (source == col0Editor) { - refItemHandler.rebuildTableColumns(); + refTableColumnHandler.rebuildTableColumns(); } else if (source == profileBox || source == profileRB) { - ProfileConfig aConfig; - - aConfig = profileBox.getChosenItem(); - refItemHandler.setOrderAndConfig(aConfig.getItems()); - refItemHandler.rebuildTableColumns(); - - myItemProcessor.setItems(refItemHandler.getSortedAttributes()); + var tmpConfig = profileBox.getChosenItem(); + refTableColumnHandler.setOrderAndConfig(tmpConfig.getItems()); + refTableColumnHandler.rebuildTableColumns(); + + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); } else if (source == profilePanel && aEvent.getID() == AddProfilePanel.ID_ACCEPT) { - ProfileConfig aProfile; - Collection aItemList; - String aName; - - aName = profilePanel.getInput(); - aItemList = myItemProcessor.getItems(); - for (QueryAttribute aAttribute : aItemList) + var tmpName = profilePanel.getInput(); + var tmpItemL = myItemProcessor.getAllItems(); + for (var aAttribute : myItemProcessor.getAllItems()) aAttribute.synchronizeAttribute(); - - aProfile = new ProfileConfig(aName, aItemList); - profileBox.addItem(aProfile); + + // Determine if this will result in a Profile being replaced + ProfileConfig repProfile = null; + for (ProfileConfig aProfile : profileBox.getAllItems()) + { + if (aProfile.name().equals(tmpName) == true) + repProfile = aProfile; + } + + var tmpProfile = new ProfileConfig<>(tmpName, tmpItemL); + if (repProfile == null) + profileBox.addItem(tmpProfile); + else + profileBox.replaceItem(repProfile, tmpProfile); } updateGui(); } - + + @Override + public void itemStateChanged(ItemEvent aEvent) + { + var source = aEvent.getSource(); + if (source == profileRB) + { + var tmpConfig = profileBox.getChosenItem(); + refTableColumnHandler.setOrderAndConfig(tmpConfig.getItems()); + refTableColumnHandler.rebuildTableColumns(); + + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); + } + + updateGui(); + } + + @Override + public void valueChanged(ListSelectionEvent aEvent) + { + // Update only after the user has released the mouse + if (aEvent.getValueIsAdjusting() == true) + return; + + updateGui(); + } + @Override public void zioRead(ZinStream aStream) throws IOException { - ProfileConfig aProfile; - int numItems, profileIndex; - boolean aBool; - super.zioRead(aStream); - + aStream.readVersion(0); - - aBool = aStream.readBool(); - customRB.setSelected(aBool); - profileRB.setSelected(!aBool); - - numItems = aStream.readInt(); + + var tmpBool = aStream.readBool(); + customRB.setSelected(tmpBool); + profileRB.setSelected(!tmpBool); + + var numItems = aStream.readInt(); profileBox.removeAllItems(); for (int c1 = 0; c1 < numItems; c1++) { - aProfile = new ProfileConfig("unnamed", myItemProcessor.getItems()); - aProfile.zioRead(aStream); - - profileBox.addItem(aProfile); + var tmpProfileConfig = ProfileConfig.zioRead(aStream, myItemProcessor.getAllItems()); + profileBox.addItem(tmpProfileConfig); } - - profileIndex = aStream.readInt(); + + var profileIndex = aStream.readInt(); profileBox.removeActionListener(this); - if (profileIndex >= 0) + if (profileIndex >= 0) profileBox.setSelectedIndex(profileIndex); profileBox.addActionListener(this); - - refItemHandler.zioRead(aStream); - + + var tmpProfileConfig = ProfileConfig.zioRead(aStream, myItemProcessor.getAllItems()); + refTableColumnHandler.setOrderAndConfig(tmpProfileConfig.getItems()); + refTableColumnHandler.rebuildTableColumns(); + refTableColumnHandler.setSortPriorityList(tmpProfileConfig.sortPriorityList()); + + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); + updateGui(); } @Override public void zioWrite(ZoutStream aStream) throws IOException { - int numItems, profileIndex; - boolean aBool; - super.zioWrite(aStream); aStream.writeVersion(0); - - aBool = customRB.isSelected(); - aStream.writeBool(aBool); - - numItems = profileBox.getAllItems().size(); + + var tmpBool = customRB.isSelected(); + aStream.writeBool(tmpBool); + + var numItems = profileBox.getAllItems().size(); aStream.writeInt(numItems); - - for (ProfileConfig aProfile : profileBox.getAllItems()) - { - aProfile.zioWrite(aStream); - } - - profileIndex = profileBox.getSelectedIndex(); + + for (var aProfile : profileBox.getAllItems()) + ProfileConfig.zioWrite(aStream, aProfile); + + var profileIndex = profileBox.getSelectedIndex(); aStream.writeInt(profileIndex); - - refItemHandler.zioWrite(aStream); - } - @Override - public void valueChanged(ListSelectionEvent e) - { - // Update only after the user has released the mouse - if (e.getValueIsAdjusting() == true) - return; - - updateGui(); + var tmpProfileConfig = new ProfileConfig<>("", refTableColumnHandler.getOrderedAttributes(), + refTableColumnHandler.getSortPriorityList()); + ProfileConfig.zioWrite(aStream, tmpProfileConfig); } /** - * Builds the main GUI area + * Helper method that builds the main GUI area */ - protected void buildGuiArea() + private void buildGuiArea() { - JPanel tmpPanel; - ProfileConfig aConfig; - // Form the layout setLayout(new MigLayout("", "[left][grow][]", "[][][]3[grow][]")); @@ -275,121 +299,130 @@ public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj add(titleL, "growx,span 2,wrap"); // Profile Area - profileRB = GuiUtil.createJRadioButton("Profile:", this, smallFont); + profileRB = GuiUtil.createJRadioButton(this, "Profile:"); add(profileRB, "span 1"); - profileBox = new GComboBox(); + profileBox = new GComboBox<>(); profileBox.addActionListener(this); - profileBox.setFont(smallFont); add(profileBox, "growx,span 1"); - - deleteB = GuiUtil.createJButton(new DeleteIcon(14), this); - add(deleteB, "align right,span 1,w 20!, h 20!,wrap"); - - // Custom Area - customRB = GuiUtil.createJRadioButton("Custom:", this, smallFont); - customRB.setSelected(true); - add(customRB, "span 1"); - //saveB = GuiUtil.createJButton(new ArrowNorthIcon(14), this); - //add(saveB, "align right,span 1,w 18!, h 18!"); - saveB = GuiUtil.createJButton("Save", this, smallFont); + deleteB = GuiUtil.createJButton(new DeleteIcon(14), this); + add(deleteB, "align right,span 1,w 20!,h 20!,wrap"); + + // Custom Area + customRB = GuiUtil.createJRadioButton(this, "Custom:"); + add(customRB, ""); + + // saveB = GuiUtil.createJButton(new ArrowNorthIcon(14), this); + // add(saveB, "align right,span 1,w 18!, h 18!"); + saveB = GuiUtil.createJButton("Save", this); add(saveB, "align right,span 2,wrap"); - - tmpPanel = buildItemListTablePanel(); + + var tmpPanel = buildItemListTablePanel(); tmpPanel.setBorder(new EmptyBorder(0, 15, 0, 0)); add(tmpPanel, "growx,growy,span,wrap"); // Link the radio buttons GuiUtil.linkRadioButtons(profileRB, customRB); - + // Build the config area tmpPanel = buildConfigPanel(); add(tmpPanel, "growx,span,wrap"); // Action area - closeB = GuiUtil.createJButton("Close", this, smallFont); + closeB = GuiUtil.createJButton("Close", this); add(closeB, "align right,span"); // Add in the default profile - aConfig = new ProfileConfig(AddProfilePanel.DEFAULT_NAME, refItemHandler.getSortedAttributes()); - profileBox.addItem(aConfig); + var tmpConfig = new ProfileConfig<>(ProfileConfig.DEFAULT_NAME, refTableColumnHandler.getOrderedAttributes()); + profileBox.addItem(tmpConfig); - setBorder(new BevelBorder(BevelBorder.RAISED)); + customRB.setSelected(true); } /** - * Utility method to build the configuration area for the individual attributes + * Helper method to build the configuration area for the individual attributes */ - protected JPanel buildConfigPanel() + private JPanel buildConfigPanel() { - JPanel tmpPanel; - // Form the layout - tmpPanel = new JPanel(); + var tmpPanel = new JPanel(); tmpPanel.setLayout(new MigLayout("", "0[grow][][]0", "0[][]10")); // Title Area - labelL = GuiUtil.createJLabel("Label:", smallFont); + labelL = new JLabel("Label:", JLabel.LEADING); tmpPanel.add(labelL, "growx,span 1"); - + upB = GuiUtil.createJButton(new ArrowNorthIcon(14), this); tmpPanel.add(upB, "align right,span 1,w 18!, h 18!"); - + downB = GuiUtil.createJButton(new ArrowSouthIcon(14), this); tmpPanel.add(downB, "align right,span 1,w 18!, h 18!,wrap"); - - labelTF = new GTextField(this); + + labelTF = new GTextField(this); tmpPanel.add(labelTF, "growx,span 3,wrap"); - + return tmpPanel; } /** - * Utility method to build the query item list table + * Helper method to build the query item list table */ - protected JPanel buildItemListTablePanel() + private JPanel buildItemListTablePanel() { - QueryComposer aComposer; - ItemHandler aItemHandler; - - aComposer = new QueryComposer(); - aComposer.addAttribute(ConfigLookUp.IsVisible, Boolean.class, "", ""); - aComposer.addAttribute(ConfigLookUp.Name, String.class, "Name", null); - aComposer.addAttribute(ConfigLookUp.Label, String.class, "Label", null); - + var tmpComposer = new QueryComposer(); + tmpComposer.addAttribute(ConfigLookUp.IsVisible, Boolean.class, "", ""); + tmpComposer.addAttribute(ConfigLookUp.Name, String.class, "Name", null); + tmpComposer.addAttribute(ConfigLookUp.Label, String.class, "Label", null); + col0Editor = new BooleanCellEditor(); col0Editor.addActionListener(this); col0Renderer = new BooleanCellRenderer(); col1Renderer = new DefaultTableCellRenderer(); - aComposer.setEditor(ConfigLookUp.IsVisible, col0Editor); - aComposer.setRenderer(ConfigLookUp.IsVisible, col0Renderer); - aComposer.setRenderer(ConfigLookUp.Name, col1Renderer); - aComposer.setRenderer(ConfigLookUp.Label, col1Renderer); - - aItemHandler = new ConfigHandler(aComposer); - - myItemProcessor = new StaticItemProcessor(); - myItemProcessor.setItems(refItemHandler.getSortedAttributes()); - - listPanel = new ItemListPanel(aItemHandler, myItemProcessor, false, false); + tmpComposer.setEditor(ConfigLookUp.IsVisible, col0Editor); + tmpComposer.setRenderer(ConfigLookUp.IsVisible, col0Renderer); + tmpComposer.setRenderer(ConfigLookUp.Name, col1Renderer); + tmpComposer.setRenderer(ConfigLookUp.Label, col1Renderer); + + var tmpIH = new ConfigHandler(); + myItemProcessor = new StaticItemProcessor<>(); + myItemProcessor.setItems(refTableColumnHandler.getOrderedAttributes()); + + listPanel = new ItemListPanel<>(tmpIH, myItemProcessor, tmpComposer, false); listPanel.setSortingEnabled(false); listPanel.addListSelectionListener(this); return listPanel; } - + + /** + * Helper method that executes the delete profile action. + */ + private void doActionDel() + { + var tmpPick = profileBox.getChosenItem(); + if (tmpPick.name() == ProfileConfig.DEFAULT_NAME) + { + var msgPanel = new MessagePanel(this, "Reserved Profile"); + msgPanel.setInfo("The Default profile can not be deleted."); + msgPanel.setSize(400, 140); + msgPanel.setVisibleAsModal(); + return; + } + + profileBox.removeItem(profileBox.getChosenItem()); + actionPerformed(new ActionEvent(profileBox, ID_UPDATE, null)); + } + /** * Synchronizes the model to match the label gui */ - protected void updateLayerAttribute() + private void updateLayerAttribute() { - QueryAttribute chosenItem; - - chosenItem = listPanel.getSelectedItem(); + var chosenItem = listPanel.getSelectedItem(); chosenItem.label = labelTF.getText(); - + chosenItem.assocTableColumn.setHeaderValue(chosenItem.label); - + listPanel.repaint(); //getParent().repaint(); } @@ -397,16 +430,12 @@ public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj /** * Synchronizes our GUI vars */ - protected void updateGui() + private void updateGui() { - QueryAttribute chosenItem; - boolean isEnabled; - int chosenIndex; - // Update the profile area - isEnabled = profileRB.isSelected(); + var isEnabled = profileRB.isSelected(); profileBox.setEnabled(isEnabled); - + isEnabled = isEnabled & (profileBox.getAllItems().size() > 1); deleteB.setEnabled(isEnabled); @@ -416,20 +445,20 @@ public class EditTablePanel extends GlassPanel implements ActionListener, ZioObj GuiUtil.setEnabled(listPanel, isEnabled); col0Renderer.setEnabled(isEnabled); col1Renderer.setEnabled(isEnabled); - - chosenItem = listPanel.getSelectedItem(); - chosenIndex = myItemProcessor.indexOf(chosenItem); + + var chosenItem = listPanel.getSelectedItem(); + var chosenIndex = myItemProcessor.indexOf(chosenItem); if (chosenItem != null) labelTF.setText(chosenItem.label); isEnabled = isEnabled & (chosenItem != null); labelL.setEnabled(isEnabled); labelTF.setEnabled(isEnabled); - - upB.setEnabled(isEnabled & (chosenIndex > 0)); - downB.setEnabled(isEnabled & (chosenIndex+1 < myItemProcessor.getNumItems())); - listPanel.repaint(); + upB.setEnabled(isEnabled & (chosenIndex > 0)); + downB.setEnabled(isEnabled & (chosenIndex + 1 < myItemProcessor.getNumItems())); + + listPanel.repaint(); } } diff --git a/src/glum/gui/panel/itemList/config/EditTablePanelClassic.java b/src/glum/gui/panel/itemList/config/EditTablePanelClassic.java index 5cacc6f..83301bf 100644 --- a/src/glum/gui/panel/itemList/config/EditTablePanelClassic.java +++ b/src/glum/gui/panel/itemList/config/EditTablePanelClassic.java @@ -1,44 +1,74 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; -import java.awt.*; -import java.awt.event.*; -import java.util.*; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.HashMap; +import java.util.Map; + import javax.swing.*; import javax.swing.border.*; -import glum.gui.*; -import glum.gui.action.*; +import glum.gui.FocusUtil; +import glum.gui.GuiUtil; +import glum.gui.action.ClickAction; import glum.gui.component.GComboBox; -import glum.gui.panel.*; -import glum.gui.panel.itemList.query.*; - +import glum.gui.panel.GlassPanel; +import glum.gui.panel.itemList.TableColumnHandler; +import glum.gui.panel.itemList.query.QueryAttribute; +import glum.gui.panel.itemList.query.QueryItemHandler; import net.miginfocom.swing.MigLayout; -public class EditTablePanelClassic extends GlassPanel implements ActionListener +/** + * UI component that allows for the configuration of ItemListPanels that utilize the {@link QueryAttribute} and + * {@link QueryItemHandler} interfaces. + *

    + * New code should make use of {@link EditTablePanel} rather than {@link EditTablePanelClassic}. + * + * @author lopeznr1 + */ +public class EditTablePanelClassic> extends GlassPanel implements ActionListener { + // Attributes + private final TableColumnHandler refTableColumnHandler; + // GUI vars - protected JLabel titleL; - protected JRadioButton profileRB, customRB; - protected GComboBox profileBox; - protected JButton closeB; + private JLabel titleL; + private JRadioButton profileRB, customRB; + private GComboBox> profileBox; + private JButton closeB; // State vars - protected QueryItemHandler refItemHandler; - protected Map actionMap; + private Map> actionMap; - public EditTablePanelClassic(Component aParent, QueryItemHandler aItemHandler) + /** Standard Constructor */ + public EditTablePanelClassic(Component aParent, TableColumnHandler aTableColumnHandler) { super(aParent); // State vars - refItemHandler = aItemHandler; - actionMap = new HashMap(); + refTableColumnHandler = aTableColumnHandler; + actionMap = new HashMap<>(); // Build the actual GUI buildGuiArea(); setPreferredSize(new Dimension(200, getPreferredSize().height)); updateGui(); - + // Set up some keyboard shortcuts FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(closeB)); } @@ -46,10 +76,7 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener @Override public void actionPerformed(ActionEvent e) { - Object source; - - source = e.getSource(); - + var source = e.getSource(); if (source == closeB) { setVisible(false); @@ -57,35 +84,29 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener } else if (source == profileBox || source == profileRB) { - ProfileConfig aConfig; - QueryAttribute aAttribute; - - aConfig = profileBox.getChosenItem(); - refItemHandler.setOrderAndConfig(aConfig.getItems()); - refItemHandler.rebuildTableColumns(); - + var tmpConfig = profileBox.getChosenItem(); + refTableColumnHandler.setOrderAndConfig(tmpConfig.getItems()); + refTableColumnHandler.rebuildTableColumns(); + for (JCheckBox itemCB : actionMap.keySet()) { - aAttribute = actionMap.get(itemCB); - itemCB.setSelected(aAttribute.isVisible); + var tmpAttribute = actionMap.get(itemCB); + itemCB.setSelected(tmpAttribute.isVisible); } } else if (source instanceof JCheckBox) { - QueryAttribute aAttribute; - JCheckBox tmpCB; - - tmpCB = (JCheckBox)source; - aAttribute = actionMap.get(tmpCB); - aAttribute.isVisible = tmpCB.isSelected(); - - refItemHandler.rebuildTableColumns(); + var tmpCB = (JCheckBox) source; + var tmpAttribute = actionMap.get(tmpCB); + tmpAttribute.isVisible = tmpCB.isSelected(); + + refTableColumnHandler.rebuildTableColumns(); } updateGui(); } - public void addConfig(ProfileConfig aConfig) + public void addConfig(ProfileConfig aConfig) { profileBox.addItem(aConfig); } @@ -95,9 +116,6 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener */ protected void buildGuiArea() { - JPanel tmpPanel; - Border border1, border2; - // Form the grid bag constraints setLayout(new MigLayout("", "[left][grow]", "[]")); @@ -110,7 +128,7 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener profileRB.addActionListener(this); add(profileRB, "span 1"); - profileBox = new GComboBox(); + profileBox = new GComboBox<>(); profileBox.addActionListener(this); add(profileBox, "growx,span 1,wrap"); @@ -119,9 +137,9 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener customRB.addActionListener(this); add(customRB, "span 1,wrap"); - tmpPanel = buildQueryItemPanel(); - border1 = new EmptyBorder(0, 10, 0, 10); - border2 = new BevelBorder(BevelBorder.RAISED); + var tmpPanel = buildQueryItemPanel(); + var border1 = new EmptyBorder(0, 10, 0, 10); + var border2 = new BevelBorder(BevelBorder.RAISED); tmpPanel.setBorder(new CompoundBorder(border2, border1)); tmpPanel.setEnabled(false); add(tmpPanel, "growx,span 2,wrap"); @@ -130,45 +148,36 @@ public class EditTablePanelClassic extends GlassPanel implements ActionListener GuiUtil.linkRadioButtons(profileRB, customRB); // Build the default profile box - ProfileConfig aConfig; - aConfig = new ProfileConfig(AddProfilePanel.DEFAULT_NAME, refItemHandler.getSortedAttributes()); - addConfig(aConfig); + var tmpConfig = new ProfileConfig<>(ProfileConfig.DEFAULT_NAME, refTableColumnHandler.getOrderedAttributes()); + addConfig(tmpConfig); // Action area closeB = GuiUtil.createJButton("Close", this); - add(closeB, "align right,span 2"); - - setBorder(new BevelBorder(BevelBorder.RAISED)); + add(closeB, "ax right,span,split"); } protected JPanel buildQueryItemPanel() { - Collection attrList; - JPanel aPanel; - JCheckBox tmpCB; + var retPanel = new JPanel(); + retPanel.setLayout(new BoxLayout(retPanel, BoxLayout.Y_AXIS)); - aPanel = new JPanel(); - aPanel.setLayout(new BoxLayout(aPanel, BoxLayout.Y_AXIS)); - - attrList = refItemHandler.getSortedAttributes(); - for (QueryAttribute aAttr : attrList) + var tmpAttrL = refTableColumnHandler.getOrderedAttributes(); + for (QueryAttribute aAttr : tmpAttrL) { - tmpCB = new JCheckBox(aAttr.label, aAttr.isVisible); + var tmpCB = new JCheckBox(aAttr.label, aAttr.isVisible); tmpCB.addActionListener(this); - aPanel.add(tmpCB); + retPanel.add(tmpCB); actionMap.put(tmpCB, aAttr); } - return aPanel; + return retPanel; } protected void updateGui() { - boolean isEnabled; - // Update the profile area - isEnabled = profileRB.isSelected(); + var isEnabled = profileRB.isSelected(); profileBox.setEnabled(isEnabled); // Update the custom area diff --git a/src/glum/gui/panel/itemList/config/ProfileConfig.java b/src/glum/gui/panel/itemList/config/ProfileConfig.java index 4dec909..8b14cae 100644 --- a/src/glum/gui/panel/itemList/config/ProfileConfig.java +++ b/src/glum/gui/panel/itemList/config/ProfileConfig.java @@ -1,150 +1,186 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.config; -import glum.gui.panel.itemList.query.QueryAttribute; -import glum.gui.panel.itemList.query.QueryComposer; -import glum.zio.*; - import java.io.IOException; import java.util.*; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; +import com.google.common.collect.ImmutableList; + +import glum.gui.panel.itemList.query.QueryAttribute; +import glum.zio.ZinStream; +import glum.zio.ZoutStream; /** + * Class that stores an ordered list of {@link QueryAttribute}s and the corresponding sort priorities. * - * Class that stores a list of QuerryAttributes used to describe a specific configuration - * of ItemListPanel and associated table. - * + * @author lopeznr1 */ -public class ProfileConfig implements ZioObj +public class ProfileConfig> { - // State vars - protected String myName; - protected Map itemMap; + // Constants + /** String constant that defines the reserved profile name. */ + public static final String DEFAULT_NAME = "Default"; + + // Attributes + private final String name; + private Map> itemM; + private final ImmutableList sortPriorityL; /** - * Constructor. Form a deep copy of the list of QueryAttributes. This is to - * ensure that this profile will not share QueryAttributes with other profiles - * leading to inadvertent tampering. - * - * @param aName: Name of the profile - * @param aItemSet: Set of all QuerryAttributes associated with this profile - */ - public ProfileConfig(String aName, Collection aItemList) + * Standard Constructor. Form a deep copy of the list of {@link QueryAttribute}s. This is to ensure that this profile + * will not share {@link QueryAttribute}s with other profiles leading to inadvertent tampering. + * + * @param aName: + * Name of the profile + * @param aItemSet: + * Set of all QuerryAttributes associated with this profile + */ + public ProfileConfig(String aName, Collection> aItemC, Collection aSortPriorityC) { - myName = aName; - - itemMap = Maps.newLinkedHashMap(); - for (QueryAttribute aItem : aItemList) - itemMap.put(aItem.modelIndex, new QueryAttribute(aItem)); - } - - /** - * Constructor. All of the QueryAttribute will be turned off by default. - * It is imperative that you call setVisibleItems() to allow this profile - * to display columns when activated. - * - * @param aName: Name of the profile - * @param aComposer: The composer which provides all QuerryAttributes - * associated with this profile - */ - public ProfileConfig(String aName, QueryComposer aComposer) - { - this(aName, aComposer.getItems()); + name = aName; - for (QueryAttribute aItem : itemMap.values()) - aItem.isVisible = false; + itemM = new LinkedHashMap<>(); + for (QueryAttribute aItem : aItemC) + itemM.put(aItem.modelIndex, new QueryAttribute<>(aItem)); + + sortPriorityL = ImmutableList.copyOf(aSortPriorityC); + + // TODO: Make this object fully immutable } - + /** - * Sets the specified QueryAttributes as visible for this ProfileConfig + * Simplified Constructor. Form a deep copy of the list of {@link QueryAttribute}s. This is to ensure that this + * profile will not share {@link QueryAttribute}s with other profiles leading to inadvertent tampering. + * + * @param aName: + * Name of the profile + * @param aItemSet: + * Set of all QuerryAttributes associated with this profile */ - public void setVisibleItems(Collection aItemList) + public ProfileConfig(String aName, Collection> aItemL) { - for (QueryAttribute aItem : aItemList) - itemMap.get(aItem.modelIndex).isVisible = true; - } - - /** - * Sets the specified QueryAttributes as visible for this ProfileConfig - */ - public void setVisibleItems(QueryAttribute... aItemList) - { - for (QueryAttribute aItem : aItemList) - itemMap.get(aItem.modelIndex).isVisible = true; + this(aName, aItemL, ImmutableList.of()); } /** * Returns the (display) name of this ProfileConfig */ - public String getName() + public String name() { - return myName; + return name; } /** - * Returns the QueryAttributes associated with this ProfileConfig + * Returns the {@link QueryAttribute}s associated with this ProfileConfig */ - public ArrayList getItems() + public ArrayList> getItems() { - return Lists.newArrayList(itemMap.values()); + return new ArrayList<>(itemM.values()); } - @Override - public void zioRead(ZinStream aStream) throws IOException + /** + * Returns a list of model indexes which define the priority of the associated {@link QueryAttribute}s. + */ + public ImmutableList sortPriorityList() { - Map newMap; - QueryAttribute queryAttr; - int numItems, modelId; - - aStream.readVersion(0); - - myName = aStream.readString(); - - numItems = aStream.readInt(); - if (numItems != itemMap.size()) - throw new IOException("Mismatched attribute count. Expected: " + itemMap.size() + " Read: " + numItems); - - newMap = Maps.newLinkedHashMap(); - for (int c1 = 0; c1 < numItems; c1++) - { - modelId = aStream.readInt(); - - queryAttr = itemMap.get(modelId); - queryAttr.zioRead(aStream); - - newMap.put(modelId, queryAttr); - } - - itemMap = newMap; - } - - @Override - public void zioWrite(ZoutStream aStream) throws IOException - { - QueryAttribute queryAttr; - int numItems; - - aStream.writeVersion(0); - - aStream.writeString(myName); - - numItems = itemMap.size(); - aStream.writeInt(numItems); - - for (int aModelId : itemMap.keySet()) - { - queryAttr = itemMap.get(aModelId); - - aStream.writeInt(aModelId); - queryAttr.zioWrite(aStream); - } + return sortPriorityL; } @Override public String toString() { - return myName; + return name; + } + + /** + * Utility method to read the {@link ProfileConfig} from the specified stream. + */ + public static > ProfileConfig zioRead(ZinStream aStream, Collection> aItemC) + throws IOException + { + var itemM = new LinkedHashMap>(); + for (QueryAttribute aItem : aItemC) + itemM.put(aItem.modelIndex, new QueryAttribute<>(aItem)); + + aStream.readVersion(1); + + var name = aStream.readString(); + + var doRestore = true; + var numItems = aStream.readInt(); + if (numItems != itemM.size()) + { + var tmpMsg = "Mismatched attribute count. Expected: " + itemM.size() + " Read: " + numItems; + tmpMsg += "\n\tTable configuration, " + name + ", will not be restored..."; + System.err.println(tmpMsg); + doRestore = false; + } + + var newItemL = new ArrayList>(); + for (int c1 = 0; c1 < numItems; c1++) + { + var modelId = aStream.readInt(); + + var queryAttr = itemM.get(modelId); + if (queryAttr == null || doRestore == false) + { + // Skip over unsupported QueryAttributes + var tmpAttr = new QueryAttribute(null, null, -1); + tmpAttr.zioRead(aStream); + continue; + } + queryAttr.zioRead(aStream); + + newItemL.add(queryAttr); + } + + var priCnt = aStream.readInt(); + var tmpSortPriL = new ArrayList(priCnt); + for (var c1 = 0; c1 < priCnt; c1++) + tmpSortPriL.add(aStream.readInt()); + + if (doRestore == false) + { + newItemL = new ArrayList>(aItemC); + tmpSortPriL = new ArrayList(priCnt); + } + + return new ProfileConfig<>(name, newItemL, tmpSortPriL); + } + + /** + * Utility method to write the {@link ProfileConfig} to the specified stream. + */ + public static void zioWrite(ZoutStream aStream, ProfileConfig aItem) throws IOException + { + aStream.writeVersion(1); + + aStream.writeString(aItem.name); + + aStream.writeInt(aItem.itemM.size()); + for (int aModelId : aItem.itemM.keySet()) + { + var queryAttr = aItem.itemM.get(aModelId); + + aStream.writeInt(aModelId); + queryAttr.zioWrite(aStream); + } + + aStream.writeInt(aItem.sortPriorityL.size()); + for (var aModelId : aItem.sortPriorityL) + aStream.writeInt(aModelId); } } diff --git a/src/glum/gui/panel/itemList/query/QueryAttribute.java b/src/glum/gui/panel/itemList/query/QueryAttribute.java index 95f822c..9e1d94d 100644 --- a/src/glum/gui/panel/itemList/query/QueryAttribute.java +++ b/src/glum/gui/panel/itemList/query/QueryAttribute.java @@ -1,22 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.query; import java.io.IOException; +import java.util.Comparator; import javax.swing.JLabel; import javax.swing.table.*; +import glum.gui.table.SortDir; import glum.unit.EmptyUnitProvider; import glum.unit.UnitProvider; import glum.zio.*; -public class QueryAttribute implements ZioObj +/** + * Class that provides for the definition of attributes associated with a specific data field. + * + * @author lopeznr1 + */ +public class QueryAttribute> implements ZioObj { - // State vars + // Attributes + public final G1 refKey; public final int modelIndex; - public Enum refKey; - public Class refClass; + public final Class refClass; + + // State vars + public Comparator refComparator; public UnitProvider refUnitProvider; - + // Config vars public String label; public boolean isVisible; @@ -24,38 +47,52 @@ public class QueryAttribute implements ZioObj public int defaultSize; public int minSize; public int maxSize; - public int sortDir; + public SortDir sortDir; // Helper vars public TableColumn assocTableColumn; public TableCellRenderer renderer; public TableCellEditor editor; - public QueryAttribute(int aModelIndex) + /** + * Standard Constructor. + * + * @param aRefKey + * @param aRefClass + * @param aModelIndex + */ + public QueryAttribute(G1 aRefKey, Class aRefClass, int aModelIndex) { + refKey = aRefKey; + refClass = aRefClass; modelIndex = aModelIndex; - refKey = null; - refClass = String.class; - label = ""; + refComparator = null; + refUnitProvider = new EmptyUnitProvider(); + + label = ""; isVisible = true; alignment = JLabel.LEFT; defaultSize = 100; maxSize = -1; minSize = -1; - sortDir = 0; - refUnitProvider = new EmptyUnitProvider(); + sortDir = SortDir.NotSorted; assocTableColumn = null; renderer = null; editor = null; } - - public QueryAttribute(QueryAttribute aAttribute) + + /** + * Copy Constructor. + * + * @param aAttribute + */ + public QueryAttribute(QueryAttribute aAttribute) { // Synchronize the attribute before copying the configuration aAttribute.synchronizeAttribute(); - + modelIndex = aAttribute.modelIndex; refKey = aAttribute.refKey; refClass = aAttribute.refClass; @@ -70,17 +107,16 @@ public class QueryAttribute implements ZioObj refUnitProvider = aAttribute.refUnitProvider; assocTableColumn = null; - renderer = null; //aAttribute.renderer; - editor = null; //aAttribute.editor; + renderer = null; // aAttribute.renderer; + editor = null; // aAttribute.editor; } - + /** * Sets this QueryAttribute to match aAttribute. - *

    - * Currently only the following config vars are matched: - * label, isVisible, alignment, defaultSize, sortDir + *

    + * Currently only the following config vars are matched: label, isVisible, alignment, defaultSize, sortDir */ - public void setConfig(QueryAttribute aAttribute) + public void setConfig(QueryAttribute aAttribute) { label = aAttribute.label; isVisible = aAttribute.isVisible; @@ -96,7 +132,7 @@ public class QueryAttribute implements ZioObj { if (assocTableColumn == null) return; - + defaultSize = assocTableColumn.getWidth(); // sortDir = assocTableColumn. } @@ -109,7 +145,7 @@ public class QueryAttribute implements ZioObj if (assocTableColumn == null) return; -System.out.println("Are we ready for this ???"); + System.out.println("Are we ready for this ???"); assocTableColumn.setWidth(defaultSize); // sortDir = assocTableColumn. } @@ -118,14 +154,12 @@ System.out.println("Are we ready for this ???"); public void zioRead(ZinStream aStream) throws IOException { aStream.readVersion(0); - + label = aStream.readString(); isVisible = aStream.readBool(); alignment = aStream.readInt(); defaultSize = aStream.readInt(); - minSize = aStream.readInt(); - maxSize = aStream.readInt(); - sortDir = aStream.readInt(); + sortDir = aStream.readEnum(SortDir.values()); } @Override @@ -133,16 +167,14 @@ System.out.println("Are we ready for this ???"); { // Synchronize the attribute before serialization synchronizeAttribute(); - + aStream.writeVersion(0); - + aStream.writeString(label); aStream.writeBool(isVisible); aStream.writeInt(alignment); aStream.writeInt(defaultSize); - aStream.writeInt(minSize); - aStream.writeInt(maxSize); - aStream.writeInt(sortDir); + aStream.writeEnum(sortDir); } - + } diff --git a/src/glum/gui/panel/itemList/query/QueryComposer.java b/src/glum/gui/panel/itemList/query/QueryComposer.java index a9d6dfa..6284e15 100644 --- a/src/glum/gui/panel/itemList/query/QueryComposer.java +++ b/src/glum/gui/panel/itemList/query/QueryComposer.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.query; import java.util.*; @@ -9,34 +22,44 @@ import javax.swing.table.TableCellRenderer; import com.google.common.base.Preconditions; +import glum.gui.panel.itemList.ItemListPanel; import glum.unit.UnitProvider; +/** + * Object which allows a collection of QueryAttributes to be programmatically constructed. This object provides for a + * simplified and uniform method for construction the {@link QueryAttribute}s that will be associated with a + * {@link ItemListPanel}. + * + * @param + * + * @author lopeznr1 + */ public class QueryComposer> { // State vars - protected ArrayList itemList; -// protected Map itemMap; + protected ArrayList> itemL; +// protected Map> itemM; public QueryComposer() { - itemList = new ArrayList<>(); -// itemMap = Maps.newLinkedHashMap(); + itemL = new ArrayList<>(); +// itemM = new LinkedHashMap<>(); } /** * Return the QueryAttribute located at aIndex */ - public QueryAttribute get(int aIndex) + public QueryAttribute get(int aIndex) { - return itemList.get(aIndex); + return itemL.get(aIndex); } /** * Return the QueryAttribute associated with aRefKey */ - public QueryAttribute getItem(G1 aRefKey) + public QueryAttribute getItem(G1 aRefKey) { - for (QueryAttribute aItem : itemList) + for (QueryAttribute aItem : itemL) { if (aItem.refKey == aRefKey) return aItem; @@ -48,39 +71,39 @@ public class QueryComposer> /** * Returns a listing of all the QueryAttributes that were composed */ - public Collection getItems() + public Collection> getItems() { - return new ArrayList<>(itemList); + return new ArrayList<>(itemL); } /** * Returns a listing of the items found between sIndex, eIndex (inclusive) */ - public Collection getItemsFrom(int sIndex, int eIndex) + public Collection> getItemsFrom(int sIndex, int eIndex) { - List rList; + List> retL; - rList = new ArrayList<>((eIndex - sIndex) + 1); + retL = new ArrayList<>((eIndex - sIndex) + 1); for (int c1 = sIndex; c1 <= eIndex; c1++) - rList.add(itemList.get(c1)); + retL.add(itemL.get(c1)); - return rList; + return retL; } /** * Returns a listing of the items found between sKey, eKey (inclusive) */ - public Collection getItemsFrom(G1 sKey, G1 eKey) + public Collection> getItemsFrom(G1 sKey, G1 eKey) { int sIndex, eIndex; - // Locate the indexes for the coresponding elements + // Locate the indexes for the corresponding elements sIndex = eIndex = -1; - for (int c1 = 0; c1 < itemList.size(); c1++) + for (int c1 = 0; c1 < itemL.size(); c1++) { - if (itemList.get(c1).refKey == sKey) + if (itemL.get(c1).refKey == sKey) sIndex = c1; - if (itemList.get(c1).refKey == eKey) + if (itemL.get(c1).refKey == eKey) eIndex = c1; } @@ -100,35 +123,36 @@ public class QueryComposer> */ public int size() { - return itemList.size(); + return itemL.size(); } /** * Method to add a QueryAttribute to this container */ - public QueryAttribute addAttribute(G1 aRefKey, UnitProvider aUnitProvider, String aName, String maxValue) + public QueryAttribute addAttribute(G1 aRefKey, UnitProvider aUnitProvider, String aName, String maxValue) { return addAttribute(aRefKey, aUnitProvider, aName, maxValue, true); } - public QueryAttribute addAttribute(G1 aRefKey, UnitProvider aUnitProvider, String aName, String maxValue, boolean isVisible) + public QueryAttribute addAttribute(G1 aRefKey, UnitProvider aUnitProvider, String aName, String maxValue, + boolean isVisible) { - QueryAttribute aAttribute; + QueryAttribute retAttribute; // Insanity check Preconditions.checkNotNull(aUnitProvider); - aAttribute = addAttribute(aRefKey, Double.class, aName, maxValue, isVisible); - aAttribute.refUnitProvider = aUnitProvider; - return aAttribute; + retAttribute = addAttribute(aRefKey, Double.class, aName, maxValue, isVisible); + retAttribute.refUnitProvider = aUnitProvider; + return retAttribute; } - public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, String maxValue) + public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, String maxValue) { return addAttribute(aRefKey, aClass, aName, maxValue, true); } - public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, String maxValue, boolean isVisible) + public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, String maxValue, boolean isVisible) { int maxSize; @@ -140,14 +164,14 @@ public class QueryComposer> return addAttribute(aRefKey, aClass, aName, maxSize, isVisible); } - public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, int aMaxSize) + public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aName, int aMaxSize) { return addAttribute(aRefKey, aClass, aName, aMaxSize, true); } - public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aLabel, int aMaxSize, boolean isVisible) + public QueryAttribute addAttribute(G1 aRefKey, Class aClass, String aLabel, int aMaxSize, boolean isVisible) { - QueryAttribute aAttribute; + QueryAttribute retAttribute; int defaultSize, minSize, maxSize; // Get the defaultSize @@ -167,28 +191,26 @@ public class QueryComposer> defaultSize = maxSize; // Form the attribute - aAttribute = new QueryAttribute(itemList.size()); - aAttribute.refKey = aRefKey; - aAttribute.refClass = aClass; - aAttribute.label = aLabel; - aAttribute.defaultSize = defaultSize; - aAttribute.maxSize = maxSize; - aAttribute.minSize = minSize; - aAttribute.isVisible = isVisible; + retAttribute = new QueryAttribute<>(aRefKey, aClass, itemL.size()); + retAttribute.label = aLabel; + retAttribute.defaultSize = defaultSize; + retAttribute.maxSize = maxSize; + retAttribute.minSize = minSize; + retAttribute.isVisible = isVisible; - itemList.add(aAttribute); - return aAttribute; + itemL.add(retAttribute); + return retAttribute; } - public QueryAttribute addAttribute(G1 aRefKey, Enum enumSet[], String aName) + public QueryAttribute addAttribute(G1 aRefKey, Enum enumSet[], String aName) { return addAttribute(aRefKey, enumSet, aName, true); } - public QueryAttribute addAttribute(G1 aRefKey, Enum enumSet[], String aLabel, boolean isVisible) + public QueryAttribute addAttribute(G1 aRefKey, Enum enumSet[], String aLabel, boolean isVisible) { - QueryAttribute aAttribute; - int defaultSize, aSize; + QueryAttribute retAttribute; + int defaultSize, tmpSize; // Get the defaultSize defaultSize = 10; @@ -197,23 +219,21 @@ public class QueryComposer> for (Enum aEnum : enumSet) { - aSize = computeStringWidth(aEnum.toString()); - if (aSize > defaultSize) - defaultSize = aSize; + tmpSize = computeStringWidth(aEnum.toString()); + if (tmpSize > defaultSize) + defaultSize = tmpSize; } // Form the attribute - aAttribute = new QueryAttribute(itemList.size()); - aAttribute.refKey = aRefKey; - aAttribute.refClass = Enum.class; - aAttribute.label = aLabel; - aAttribute.defaultSize = defaultSize; - aAttribute.maxSize = defaultSize; - aAttribute.minSize = 15; - aAttribute.isVisible = isVisible; + retAttribute = new QueryAttribute<>(aRefKey, Enum.class, itemL.size()); + retAttribute.label = aLabel; + retAttribute.defaultSize = defaultSize; + retAttribute.maxSize = defaultSize; + retAttribute.minSize = 15; + retAttribute.isVisible = isVisible; - itemList.add(aAttribute); - return aAttribute; + itemL.add(retAttribute); + return retAttribute; } /** @@ -221,7 +241,7 @@ public class QueryComposer> */ public void setEditor(G1 aRefKey, TableCellEditor aEditor) { - for (QueryAttribute aItem : itemList) + for (QueryAttribute aItem : itemL) { if (aItem.refKey == aRefKey) { @@ -238,16 +258,18 @@ public class QueryComposer> */ public void setRenderer(G1 aRefKey, TableCellRenderer aRenderer) { - for (QueryAttribute aItem : itemList) + boolean isPass = false; + for (QueryAttribute aItem : itemL) { if (aItem.refKey == aRefKey) { aItem.renderer = aRenderer; - return; + isPass = true; } } - throw new RuntimeException("No item found with the key:" + aRefKey); + if (isPass == false) + throw new RuntimeException("No item found with the key:" + aRefKey); } /** diff --git a/src/glum/gui/panel/itemList/query/QueryItemHandler.java b/src/glum/gui/panel/itemList/query/QueryItemHandler.java index 61de190..10c6c34 100644 --- a/src/glum/gui/panel/itemList/query/QueryItemHandler.java +++ b/src/glum/gui/panel/itemList/query/QueryItemHandler.java @@ -1,58 +1,41 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.query; -import java.util.*; - import glum.database.QueryItem; -import glum.gui.panel.itemList.BasicItemHandler; +import glum.gui.panel.itemList.ItemHandler; -public class QueryItemHandler> extends BasicItemHandler +/** + * Implementation of {@link ItemHandler} that allows for tabular access of items that implement the {@link QueryItem} + * interface. + * + * @author lopeznr1 + */ +public class QueryItemHandler, G2 extends Enum> implements ItemHandler { - public QueryItemHandler(QueryComposer aComposer) + @Override + public Object getValue(G1 aItem, G2 aEnum) { - super(aComposer.getItems()); - } - - public QueryItemHandler(Collection aQueryAttrList) - { - super(aQueryAttrList); + // Delegate + return aItem.getValue(aEnum); } @Override - public Object getColumnValue(G1 aObj, int colNum) + public void setValue(G1 aItem, G2 aEnum, Object aValue) { - QueryItem> aItem; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return null; - -// return aObj.getValue(fullAttributeList.get(colNum).refKey); - aItem = null; - if (aObj instanceof QueryItem) - { - aItem = (QueryItem>)aObj; - return aItem.getValue(fullAttributeList.get(colNum).refKey); - } - - return null; - } - - @Override - public void setColumnValue(G1 aObj, int colNum, Object aValue) - { - QueryItem> aItem; - - // Insanity check - if (colNum < 0 && colNum >= fullAttributeList.size()) - return; - -// aObj.setValue(fullAttributeList.get(colNum).refKey, aValue); - aItem = null; - if (aObj instanceof QueryItem) - { - aItem = (QueryItem>)aObj; - aItem.setValue(fullAttributeList.get(colNum).refKey, aValue); - } + // Delegate + aItem.setValue(aEnum, aValue); } } diff --git a/src/glum/gui/panel/itemList/query/QueryTableCellRenderer.java b/src/glum/gui/panel/itemList/query/QueryTableCellRenderer.java index cf8e7cc..3781fc4 100644 --- a/src/glum/gui/panel/itemList/query/QueryTableCellRenderer.java +++ b/src/glum/gui/panel/itemList/query/QueryTableCellRenderer.java @@ -1,20 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.itemList.query; import java.awt.Component; + +import javax.swing.JLabel; +import javax.swing.JTable; import javax.swing.table.DefaultTableCellRenderer; -import javax.swing.*; import glum.unit.Unit; +/** + * TableCellRenderer used to render a QueryAttribute with the appropriate unit. + * + * @author lopeznr1 + */ public class QueryTableCellRenderer extends DefaultTableCellRenderer { // State vars - protected Unit myUnit; + private Unit myUnit; - public QueryTableCellRenderer(QueryAttribute aAttribute) + public QueryTableCellRenderer(QueryAttribute aAttribute) { - super(); - myUnit = null; if (aAttribute != null) { @@ -37,12 +55,11 @@ public class QueryTableCellRenderer extends DefaultTableCellRenderer } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { - JLabel retLabel; - // No special processing is needed if no unit specified - retLabel = (JLabel)super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + var retLabel = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (myUnit == null) return retLabel; diff --git a/src/glum/gui/panel/nub/Action.java b/src/glum/gui/panel/nub/Action.java index 001f3de..29b8794 100644 --- a/src/glum/gui/panel/nub/Action.java +++ b/src/glum/gui/panel/nub/Action.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.nub; public enum Action diff --git a/src/glum/gui/panel/nub/HorizontalNub.java b/src/glum/gui/panel/nub/HorizontalNub.java index 37a78c5..10f4008 100644 --- a/src/glum/gui/panel/nub/HorizontalNub.java +++ b/src/glum/gui/panel/nub/HorizontalNub.java @@ -1,29 +1,40 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.nub; +import java.awt.*; +import java.awt.event.*; + +import javax.swing.*; + import glum.gui.panel.WaftPanel; import glum.util.MathUtil; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Graphics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; -import javax.swing.JComponent; -import javax.swing.JLayeredPane; -import javax.swing.SwingUtilities; - +/** + * UI component that provides the (top and south) control used to drag glass panels. + * + * @author lopeznr1 + */ public class HorizontalNub extends JComponent implements MouseListener, MouseMotionListener { // Constants private static final int DEFAULT_CORNER_DIM = 20; - + // Gui vars protected JComponent rootComp; protected WaftPanel waftPanel; - + // Hook vars protected Dimension hookDim; protected Point hookLoc; @@ -33,40 +44,39 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot protected Action action; protected boolean isNorth; protected int cornerDim; - + // Color vars protected Color plainColor; protected Color hookColor; protected Color armedColor; - + + /** Standard Constructor */ public HorizontalNub(JComponent aRootComp, WaftPanel aWaftPanel, boolean aIsNorth) { - super(); - rootComp = aRootComp; waftPanel = aWaftPanel; - + hookDim = null; hookLoc = null; hookPt = null; - + action = Action.None; isNorth = aIsNorth; cornerDim = DEFAULT_CORNER_DIM; - + plainColor = Color.GRAY; hookColor = Color.RED.darker(); armedColor = Color.RED.darker().darker(); - + // Register for events of interest addMouseMotionListener(this); addMouseListener(this); - + // Set the dimensions of this component setMinimumSize(new Dimension(50, 6)); setPreferredSize(new Dimension(50, 6)); } - + /** * Sets in whether there will be a sub nub to grab hold that will allow the end user to resize the associated * WaftPanel. @@ -76,10 +86,10 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot cornerDim = DEFAULT_CORNER_DIM; if (aBool == false) cornerDim = 0; - + repaint(); } - + @Override public void mouseClicked(MouseEvent aEvent) { @@ -89,19 +99,17 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot @Override public void mousePressed(MouseEvent aEvent) { - Point mousePt; - // Push the waftPanel to the top of the (stack) view if (rootComp instanceof JLayeredPane) - ((JLayeredPane)rootComp).moveToFront(waftPanel); + ((JLayeredPane) rootComp).moveToFront(waftPanel); - mousePt = aEvent.getPoint(); + var mousePt = aEvent.getPoint(); action = computeMode(mousePt); - + hookDim = new Dimension(waftPanel.getSize()); hookLoc = new Point(waftPanel.getLocation()); hookPt = SwingUtilities.convertPoint(this, mousePt, rootComp); - + repaint(); } @@ -111,7 +119,7 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot hookDim = null; hookLoc = null; hookPt = null; - + action = computeMode(aEvent.getPoint()); repaint(); } @@ -128,18 +136,18 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot // Bail if we are in an active state if (hookPt != null) return; - + action = Action.None; repaint(); } - + @Override public void mouseDragged(MouseEvent aEvent) { // Bail if we are not in an active state if (hookPt == null) return; - + // Transform the reference WaftPanel tranformWaftPanel(aEvent); } @@ -150,49 +158,43 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot action = computeMode(aEvent.getPoint()); repaint(); } - + @Override public void paint(Graphics g) { - Graphics2D g2d; - int width, height; - boolean isArmed; - - height = getHeight(); - width = getWidth(); - - g2d = (Graphics2D)g; - + var height = getHeight(); + var width = getWidth(); + + var g2d = (Graphics2D) g; + // Left nub if (cornerDim > 0) { - isArmed = action == Action.ResizeNW || action == Action.ResizeSW; - drawHandle(g2d, 0, 0, cornerDim, height, isArmed); - + var isArmed = action == Action.ResizeNW || action == Action.ResizeSW; + drawHandle(g2d, 0, 0, cornerDim, height, isArmed); + // Right nub isArmed = action == Action.ResizeNE || action == Action.ResizeSE; - drawHandle(g2d, width-cornerDim, 0, cornerDim, height, isArmed); + drawHandle(g2d, width - cornerDim, 0, cornerDim, height, isArmed); } // Middle nub - drawHandle(g2d, cornerDim, 0, width - (2 * cornerDim), height, action == Action.Move); + drawHandle(g2d, cornerDim, 0, width - (2 * cornerDim), height, action == Action.Move); } - + /** * Helper method to update the mode based on the mouse position */ private Action computeMode(Point mousePt) { - int mouseX, mouseY; - - mouseX = mousePt.x; - mouseY = mousePt.y; - + var mouseX = mousePt.x; + var mouseY = mousePt.y; + if (mouseX < 0 || mouseX >= getWidth()) return Action.None; if (mouseY < 0 || mouseY >= getHeight()) return Action.None; - + if (isNorth == true) { if (mouseX < cornerDim) @@ -216,80 +218,73 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot */ protected void drawHandle(Graphics2D g2d, int x, int y, int width, int height, boolean isArmed) { - Color aColor; - - aColor = plainColor; + var tmpColor = plainColor; if (isArmed == true) { - aColor = armedColor; + tmpColor = armedColor; if (hookPt != null) - aColor = hookColor; + tmpColor = hookColor; } - - g2d.setColor(aColor); - g2d.fill3DRect(x, y, width, height, true); + + g2d.setColor(tmpColor); + g2d.fill3DRect(x, y, width, height, true); } - + /** * Helper method to transform the associated WaftPanel based on the drag event and the current action. */ protected void tranformWaftPanel(MouseEvent aEvent) { - Dimension minDim, targDim; - Point targLoc; - Point mousePt; - Point nwPt, sePt; - int maxX, maxY; - int diffX, diffY; - - maxX = rootComp.getWidth(); - maxY = rootComp.getHeight(); - - minDim = waftPanel.getMinimumSize(); - - // Retrieve the mousePt in the rootComp coordinate system and force it to be in the bounds of the rootComp. - mousePt = aEvent.getPoint(); + var maxX = rootComp.getWidth(); + var maxY = rootComp.getHeight(); + + var minDim = waftPanel.getMinimumSize(); + + // Retrieve the mousePt in the rootComp coordinate system and force it to be in the bounds of the rootComp. + var mousePt = aEvent.getPoint(); mousePt = SwingUtilities.convertPoint(this, mousePt, rootComp); - mousePt.x = MathUtil.boundRange(0, maxX, mousePt.x); - mousePt.y = MathUtil.boundRange(0, maxY, mousePt.y); - + mousePt.x = MathUtil.boundRange(0, maxX, mousePt.x); + mousePt.y = MathUtil.boundRange(0, maxY, mousePt.y); + // Compute the total change in the mouse movement from the hooked point - diffX = -(mousePt.x - hookPt.x); - diffY = -(mousePt.y - hookPt.y); - - // Retrieve the northwest and southeast points in the root coordinate system - nwPt = new Point(0, 0); + var diffX = -(mousePt.x - hookPt.x); + var diffY = -(mousePt.y - hookPt.y); + + // Retrieve the northwest and southeast points in the root coordinate system + var nwPt = new Point(0, 0); nwPt = SwingUtilities.convertPoint(waftPanel, nwPt, rootComp); - sePt = new Point(waftPanel.getWidth(), waftPanel.getHeight()); + var sePt = new Point(waftPanel.getWidth(), waftPanel.getHeight()); sePt = SwingUtilities.convertPoint(waftPanel, sePt, rootComp); // Determine the target transformations based on the action + Point targLoc; + Dimension targDim; if (action == Action.ResizeNW) { targLoc = new Point(hookLoc.x - diffX, hookLoc.y - diffY); targDim = new Dimension(hookDim.width + diffX, hookDim.height + diffY); - + MathUtil.forceConstraints(targLoc, targDim, 0, 0, sePt.x, sePt.y, minDim, false); } else if (action == Action.ResizeNE) { targLoc = new Point(hookLoc.x, hookLoc.y - diffY); targDim = new Dimension(hookDim.width - diffX, hookDim.height + diffY); - + MathUtil.forceConstraints(targLoc, targDim, nwPt.x, 0, maxX, sePt.y, minDim, false); } else if (action == Action.ResizeSW) { targLoc = new Point(hookLoc.x - diffX, hookLoc.y); targDim = new Dimension(hookDim.width + diffX, hookDim.height - diffY); - + MathUtil.forceConstraints(targLoc, targDim, 0, nwPt.y, sePt.x, maxY, minDim, false); } else if (action == Action.ResizeSE) { targLoc = new Point(hookLoc.x, hookLoc.y); targDim = new Dimension(hookDim.width - diffX, hookDim.height - diffY); - + MathUtil.forceConstraints(targLoc, targDim, nwPt.x, nwPt.y, maxX, maxY, minDim, false); } else if (action == Action.Move) @@ -304,7 +299,7 @@ public class HorizontalNub extends JComponent implements MouseListener, MouseMot { throw new RuntimeException("Unsupported action: " + action); } - + // Update the waftPanel with the target transformations waftPanel.setLocation(targLoc); waftPanel.setPreferredSize(targDim); diff --git a/src/glum/gui/panel/task/AutoTaskPanel.java b/src/glum/gui/panel/task/AutoTaskPanel.java index 2044665..8ba6dd0 100644 --- a/src/glum/gui/panel/task/AutoTaskPanel.java +++ b/src/glum/gui/panel/task/AutoTaskPanel.java @@ -1,105 +1,112 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.task; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.component.GLabel; import glum.gui.panel.generic.GenericCodes; -import glum.unit.ConstUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.TimeCountUnit; -import glum.unit.UnitProvider; -import java.awt.Component; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; +import glum.task.Task; +import glum.unit.*; import net.miginfocom.swing.MigLayout; /** - * TaskPanel component that shows the progress of a Task. The panel itself is a Task. - * This TaskPanel will typically disappear once the progress reaches 100% or the task is - * no longer valid. This task may be stopped prematurely if the user hits the abort button. + * GUI component that shows the progress of a "task". The panel itself is a {@link Task}. This TaskPanel will typically + * disappear once the progress reaches 100% or the task is no longer valid. This task may be stopped prematurely if the + * user hits the abort button. + * + * @author lopeznr1 */ public class AutoTaskPanel extends BaseTaskPanel implements ActionListener, GenericCodes { // GUI vars private JButton abortB; - + + /** + * Standard Constructor + */ + public AutoTaskPanel(Component aParent, boolean aHasInfoArea, boolean aHasStatusArea) + { + super(aParent); + + buildGuiArea(aHasInfoArea, aHasStatusArea); + setSize(450, getPreferredSize().height); + setTitle("Task Progress"); + + // Set up keyboard short cuts + FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(abortB)); + + updateGui(); + } + + /** + * Simplified Constructor + */ public AutoTaskPanel(Component aParent) { this(aParent, false, true); } - public AutoTaskPanel(Component aParent, boolean hasInfoArea, boolean hasStatusArea) - { - super(aParent); - - buildGuiArea(hasInfoArea, hasStatusArea); - setSize(450, getPreferredSize().height); - setTitle("Task Progress"); - - // Set up keyboard short cuts - FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(abortB)); - - updateGui(); - } - @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == abortB) { abort(); - notifyListeners(this, ID_CANCEL, "Abort"); + notifyListeners(this, ID_CANCEL, "Abort"); setVisible(false); } - + updateGui(); } - + /** * Forms the actual GUI */ protected void buildGuiArea(boolean hasInfoArea, boolean hasStatusArea) { - JLabel tmpL; - JScrollPane tmpScrollPane; - UnitProvider percentUP, timerUP; - String colConstraints; - Font aFont; - Border aBorder; - - colConstraints = "[][]"; + var colConstraints = "[][]"; if (hasStatusArea == true) colConstraints += "[]"; if (hasInfoArea == true) colConstraints += "[grow][]"; setLayout(new MigLayout("", "[right][pref!][grow][right][pref!]", colConstraints)); - aFont = (new JTextField()).getFont(); - + var tmpFont = (new JTextField()).getFont(); + // Title area titleL = new JLabel("Title", JLabel.CENTER); add(titleL, "growx,span,wrap"); - + // Progress + Timer area - percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); - tmpL = new JLabel("Progress:"); - progressL = new GLabel(percentUP, aFont, true); + var percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); + var tmpL = new JLabel("Progress:"); + progressL = new GLabel(percentUP, tmpFont, true); add(tmpL, ""); add(progressL, ""); - - timerUP = new ConstUnitProvider(new TimeCountUnit(2)); + + var timerUP = new ConstUnitProvider(new TimeCountUnit(2)); tmpL = new JLabel("Time:"); - timerL = new GLabel(timerUP, aFont, true); + timerL = new GLabel(timerUP, tmpFont, true); add(tmpL, "skip 1"); add(timerL, "wrap"); @@ -107,30 +114,26 @@ public class AutoTaskPanel extends BaseTaskPanel implements ActionListener, Gene if (hasStatusArea == true) { tmpL = new JLabel("Status:"); - statusL = new GLabel(aFont); + statusL = new GLabel(tmpFont); add(tmpL, ""); add(statusL, "growx,span,wrap"); } - + // Info area infoTA = null; if (hasInfoArea == true) { - infoTA = GuiUtil.createUneditableTextArea(3, 0); - infoTA.setFont(new Font("Monospaced", Font.PLAIN, aFont.getSize()-2)); + infoTA = GuiUtil.createUneditableTextArea(3, 0); + infoTA.setFont(new Font("Monospaced", Font.PLAIN, tmpFont.getSize() - 2)); infoTA.setOpaque(true); - - tmpScrollPane = new JScrollPane(infoTA); + + var tmpScrollPane = new JScrollPane(infoTA); add(tmpScrollPane, "growx,growy,span,wrap"); } - - // Control area - abortB = GuiUtil.createJButton("Abort", this, aFont); - add(abortB, "align right,span,split 1"); - // Border - aBorder = new BevelBorder(BevelBorder.RAISED); - setBorder(aBorder); + // Control area + abortB = GuiUtil.createJButton("Abort", this); + add(abortB, "ax right,span,split"); } @Override @@ -143,7 +146,7 @@ public class AutoTaskPanel extends BaseTaskPanel implements ActionListener, Gene isActive = false; setVisible(false); } - + progressL.setValue(mProgress); if (statusL != null) statusL.setValue(mStatus); diff --git a/src/glum/gui/panel/task/BaseTaskPanel.java b/src/glum/gui/panel/task/BaseTaskPanel.java index 81df0d8..b858836 100644 --- a/src/glum/gui/panel/task/BaseTaskPanel.java +++ b/src/glum/gui/panel/task/BaseTaskPanel.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.task; import java.awt.Component; @@ -11,11 +24,14 @@ import glum.task.Task; import glum.util.WallTimer; /** - * Abstract TaskPanel that handles all of the state vars used to maintain the Task interface. + * Abstract GUI component that provides an implementation of the {@link Task} interface. + * + * @author lopeznr1 */ public abstract class BaseTaskPanel extends GlassPanel implements Task { // State vars + protected boolean isAborted; protected boolean isActive; protected String infoMsgFrag; protected double mProgress; @@ -31,10 +47,14 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task protected GLabel statusL; protected JTextArea infoTA; + /** + * Standard Constructor + */ public BaseTaskPanel(Component aParent) { super(aParent); + isAborted = false; isActive = true; infoMsgFrag = null; mProgress = 0; @@ -66,15 +86,27 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task public void abort() { mTimer.stop(); + isAborted = true; isActive = false; - Runnable tmpRunnable = () -> updateGui(); - SwingUtilities.invokeLater(tmpRunnable); + SwingUtilities.invokeLater(() -> updateGui()); } @Override - public void infoAppend(String aMsg) + public boolean isAborted() { - infoUpdateForce(aMsg); + return isAborted; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + logRegUpdateForce(aFmtMsg, aObjArr); // Reset the dynamic vars infoMsgFrag = null; @@ -82,19 +114,19 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task } @Override - public void infoAppendln(String aMsg) + public void logRegln(String aFmtMsg, Object... aObjArr) { - infoAppend(aMsg + '\n'); + logReg(aFmtMsg + '\n', aObjArr); } @Override - public void infoUpdate(String aMsg) + public void logRegUpdate(String aFmtMsg, Object... aObjArr) { // Bail if it is not time to update our UI if (isTimeForUpdate() == false) return; - infoUpdateForce(aMsg); + logRegUpdateForce(aFmtMsg, aObjArr); } @Override @@ -106,6 +138,7 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task @Override public void reset() { + isAborted = false; isActive = true; infoMsgFrag = null; mProgress = 0; @@ -117,8 +150,7 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task if (infoTA != null) infoTA.setText(""); - Runnable tmpRunnable = () -> updateGui(); - SwingUtilities.invokeLater(tmpRunnable); + SwingUtilities.invokeLater(() -> updateGui()); } @Override @@ -130,14 +162,13 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task if (isTimeForUpdate() == false && aProgress < 1.0) return; - Runnable tmpRunnable = () -> updateGui(); - SwingUtilities.invokeLater(tmpRunnable); + SwingUtilities.invokeLater(() -> updateGui()); } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - setProgress((currVal + 0.0) / maxVal); + setProgress((aCurrVal + 0.0) / aMaxVal); } @Override @@ -147,10 +178,10 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { if (infoTA != null) - infoTA.setTabSize(numSpaces); + infoTA.setTabSize(aNumSpaces); } @Override @@ -168,14 +199,7 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task if (isTimeForUpdate() == false) return; - Runnable tmpRunnable = () -> updateGui(); - SwingUtilities.invokeLater(tmpRunnable); - } - - @Override - public boolean isActive() - { - return isActive; + SwingUtilities.invokeLater(() -> updateGui()); } /** @@ -201,7 +225,7 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task /** * Utility method that does the actual updating of the previous info text with aMsg */ - protected void infoUpdateForce(String aMsg) + protected void logRegUpdateForce(String aFmtMsg, Object... aObjArr) { int end, start; int currLC; @@ -210,6 +234,8 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task if (infoTA == null) return; + String tmpMsg = String.format(aFmtMsg, aObjArr); + // Update the old message if (infoMsgFrag != null) { @@ -219,18 +245,18 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task { end = infoTA.getLineEndOffset(infoTA.getLineCount() - 1); start = end - infoMsgFrag.length(); - infoTA.replaceRange(aMsg, start, end); + infoTA.replaceRange(tmpMsg, start, end); } - catch(Exception aExp) + catch (Exception aExp) { - System.out.println("infoMsgFrag:" + infoMsgFrag.length() + " start: " + start + " end:" + end); + System.err.println("infoMsgFrag:" + infoMsgFrag.length() + " start: " + start + " end:" + end); throw new RuntimeException(aExp); } } // Just append the message else { - infoTA.append(aMsg); + infoTA.append(tmpMsg); // Trim the buffer if we exceed our maxLC if (maxLC > 0) @@ -245,9 +271,9 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task end = infoTA.getLineEndOffset(currLC - maxLC); infoTA.replaceRange("", start, end); } - catch(Exception aExp) + catch (Exception aExp) { - System.out.println("currLC:" + currLC + " maxLC:" + maxLC + " start: " + start + " end:" + end); + System.err.println("currLC:" + currLC + " maxLC:" + maxLC + " start: " + start + " end:" + end); throw new RuntimeException(aExp); } } @@ -255,7 +281,7 @@ public abstract class BaseTaskPanel extends GlassPanel implements Task } // Save off the new dynamic message fragment - infoMsgFrag = aMsg; + infoMsgFrag = tmpMsg; // timerL.setValue(mTimer.getTotal()); // SwingUtilities.invokeLater(new FunctionRunnable(timerL, "updateGui")); diff --git a/src/glum/gui/panel/task/DualTaskPanel.java b/src/glum/gui/panel/task/DualTaskPanel.java index 256c0db..f95c632 100644 --- a/src/glum/gui/panel/task/DualTaskPanel.java +++ b/src/glum/gui/panel/task/DualTaskPanel.java @@ -1,41 +1,57 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.task; import java.awt.Component; -import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; + import javax.swing.JButton; import javax.swing.JSplitPane; -import javax.swing.JTextField; - -import net.miginfocom.swing.MigLayout; import glum.gui.GuiUtil; import glum.gui.panel.GlassPanel; +import net.miginfocom.swing.MigLayout; +/** + * Panel used to display content from 2 separet {@link PlainTaskPanel}s. + * + * @author lopeznr1 + */ public class DualTaskPanel extends GlassPanel implements ActionListener { // Gui vars - private PlainTaskPanel priTask, secTask; + private final PlainTaskPanel priTask, secTask; private JButton abortB, closeB; - public DualTaskPanel(Component parentFrame, PlainTaskPanel aPriTask, PlainTaskPanel aSecTask, boolean showControlArea) + /** Standard Constructor */ + public DualTaskPanel(Component parentFrame, PlainTaskPanel aPriTask, PlainTaskPanel aSecTask, + boolean showControlArea) { super(parentFrame); - + priTask = aPriTask; secTask = aSecTask; - + buildGui(showControlArea); - updateGui(); + updateGui(); } @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == abortB) { priTask.abort(); @@ -45,31 +61,28 @@ public class DualTaskPanel extends GlassPanel implements ActionListener { setVisible(false); } - + updateGui(); } - + @Override - public void setVisible(boolean isVisible) + public void setVisible(boolean isVisible) { updateGui(); super.setVisible(isVisible); } - + /** * Forms the GUI */ private void buildGui(boolean showControlArea) { - JSplitPane mainPane; - Font smallFont; - - mainPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, priTask, secTask); + var mainPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, priTask, secTask); mainPane.setResizeWeight(0.40); - + setLayout(new MigLayout("", "[grow]", "[grow][]")); - + // Main area add(mainPane, "growx,growy,span 1,wrap"); @@ -78,24 +91,20 @@ public class DualTaskPanel extends GlassPanel implements ActionListener closeB = null; if (showControlArea == true) { - smallFont = (new JTextField()).getFont(); - - abortB = GuiUtil.createJButton("Abort", this, smallFont); + abortB = GuiUtil.createJButton("Abort", this); add(abortB, "align right,split 2"); - closeB = GuiUtil.createJButton("Close", this, smallFont); + closeB = GuiUtil.createJButton("Close", this); add(closeB); } } - + /** * Keeps the GUI synchronized */ private void updateGui() { - boolean isActive; - - isActive = priTask.isActive | secTask.isActive(); + var isActive = priTask.isActive | secTask.isActive(); if (abortB != null && closeB != null) { abortB.setEnabled(isActive); diff --git a/src/glum/gui/panel/task/FullTaskPanel.java b/src/glum/gui/panel/task/FullTaskPanel.java index c130166..e3bd710 100644 --- a/src/glum/gui/panel/task/FullTaskPanel.java +++ b/src/glum/gui/panel/task/FullTaskPanel.java @@ -1,72 +1,86 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.task; +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.*; + import glum.gui.FocusUtil; import glum.gui.GuiUtil; import glum.gui.action.ClickAction; import glum.gui.component.GLabel; import glum.gui.panel.generic.GenericCodes; -import glum.unit.ConstUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.TimeCountUnit; -import glum.unit.UnitProvider; -import java.awt.Component; -import java.awt.Font; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import javax.swing.JButton; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; -import javax.swing.border.Border; +import glum.task.Task; +import glum.unit.*; import net.miginfocom.swing.MigLayout; /** - * TaskPanel component that shows the progress of a Task. The panel itself is a Task. - * This TaskPanel must be manually closed by the user. + * GUI component that shows the progress of a "task". The panel itself is a {@link Task}. This panel must be manually + * closed by the user. + * + * @author lopeznr1 */ public class FullTaskPanel extends BaseTaskPanel implements ActionListener, GenericCodes { // GUI vars private JButton abortB, closeB; + /** + * Standard Constructor + */ + public FullTaskPanel(Component aParent, boolean hasInfoArea, boolean hasStatusArea) + { + super(aParent); + + buildGuiArea(hasInfoArea, hasStatusArea); + setSize(450, getPreferredSize().height); + setTitle("Task Progress"); + + // Set up keyboard short cuts + FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(abortB)); + FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB)); + + updateGui(); + } + + /** + * Simplified Constructor + */ public FullTaskPanel(Component aParent) { this(aParent, true, true); } - public FullTaskPanel(Component aParent, boolean hasInfoArea, boolean hasStatusArea) - { - super(aParent); - - buildGuiArea(hasInfoArea, hasStatusArea); - setSize(450, getPreferredSize().height); - setTitle("Task Progress"); - - // Set up keyboard short cuts - FocusUtil.addAncestorKeyBinding(this, "ESCAPE", new ClickAction(abortB)); - FocusUtil.addAncestorKeyBinding(this, "ENTER", new ClickAction(closeB)); - - updateGui(); - } - @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == abortB) { abort(); - notifyListeners(this, ID_CANCEL, "Abort"); + notifyListeners(this, ID_CANCEL, "Abort"); } else if (source == closeB) { setVisible(false); notifyListeners(this, ID_ACCEPT, "Close"); } - + updateGui(); } @@ -75,35 +89,28 @@ public class FullTaskPanel extends BaseTaskPanel implements ActionListener, Gene */ protected void buildGuiArea(boolean hasInfoArea, boolean hasStatusArea) { - JLabel tmpL; - JScrollPane tmpScrollPane; - UnitProvider percentUP, timerUP; - String colConstraints; - Font aFont; - Border aBorder; - - colConstraints = "[][]"; + var colConstraints = "[][]"; if (hasStatusArea == true) colConstraints += "[]"; if (hasInfoArea == true) colConstraints += "[grow][]"; setLayout(new MigLayout("", "[right][pref!][grow][right][pref!]", colConstraints)); - aFont = (new JTextField()).getFont(); - + var tmpFont = (new JTextField()).getFont(); + // Title area titleL = new JLabel("Title", JLabel.CENTER); add(titleL, "growx,span,wrap"); - + // Progress + Timer area - percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); - tmpL = new JLabel("Progress:"); - progressL = new GLabel(percentUP, aFont, true); + var percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); + var tmpL = new JLabel("Progress:"); + progressL = new GLabel(percentUP, tmpFont, true); add(tmpL, ""); add(progressL, ""); - - timerUP = new ConstUnitProvider(new TimeCountUnit(2)); + + var timerUP = new ConstUnitProvider(new TimeCountUnit(2)); tmpL = new JLabel("Time:"); - timerL = new GLabel(timerUP, aFont, true); + timerL = new GLabel(timerUP, tmpFont, true); add(tmpL, "skip 1"); add(timerL, "wrap"); @@ -111,44 +118,40 @@ public class FullTaskPanel extends BaseTaskPanel implements ActionListener, Gene if (hasStatusArea == true) { tmpL = new JLabel("Status:"); - statusL = new GLabel(aFont); + statusL = new GLabel(tmpFont); add(tmpL, ""); add(statusL, "growx,span,wrap"); } - + // Info area infoTA = null; if (hasInfoArea == true) { - infoTA = GuiUtil.createUneditableTextArea(7, 0); - infoTA.setFont(new Font("Monospaced", Font.PLAIN, aFont.getSize()-1)); + infoTA = GuiUtil.createUneditableTextArea(7, 0); + infoTA.setFont(new Font("Monospaced", Font.PLAIN, tmpFont.getSize() - 1)); infoTA.setOpaque(true); - - tmpScrollPane = new JScrollPane(infoTA); + + var tmpScrollPane = new JScrollPane(infoTA); add(tmpScrollPane, "growx,growy,span,wrap"); } - - // Control area - abortB = GuiUtil.createJButton("Abort", this, aFont); - closeB = GuiUtil.createJButton("Close", this, aFont); - add(abortB, "align right,span,split 2"); - add(closeB, "span 1"); - // Border - aBorder = new BevelBorder(BevelBorder.RAISED); - setBorder(aBorder); + // Control area + abortB = GuiUtil.createJButton("Abort", this); + closeB = GuiUtil.createJButton("Close", this); + add(abortB, "ax right,span,split"); + add(closeB, ""); } - + @Override protected void updateGui() { // If progress >= 1.0, then we are done if (mProgress >= 1.0) isActive = false; - + abortB.setEnabled(isActive); closeB.setEnabled(!isActive); - + progressL.setValue(mProgress); if (statusL != null) statusL.setValue(mStatus); diff --git a/src/glum/gui/panel/task/PlainTaskPanel.java b/src/glum/gui/panel/task/PlainTaskPanel.java index 1829e27..9cef093 100644 --- a/src/glum/gui/panel/task/PlainTaskPanel.java +++ b/src/glum/gui/panel/task/PlainTaskPanel.java @@ -1,42 +1,59 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.panel.task; +import java.awt.Component; +import java.awt.Font; + +import javax.swing.*; + import glum.gui.GuiUtil; import glum.gui.component.GLabel; -import glum.unit.ConstUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.TimeCountUnit; -import glum.unit.UnitProvider; -import java.awt.Component; -import java.awt.Font; -import javax.swing.JLabel; -import javax.swing.JScrollPane; -import javax.swing.JTextField; -import javax.swing.border.BevelBorder; +import glum.task.Task; +import glum.unit.*; import net.miginfocom.swing.MigLayout; /** - * TaskPanel component that shows the progress of a Task. The panel itself is a Task. - * This TaskPanel is typically embedded into other Components, thus it has no built in - * abort or close button. + * GUI component that shows the progress of a "task". The panel itself is a {@link Task}. This panel is typically + * embedded into other components, thus it has no built in abort or close button. + * + * @author lopeznr1 */ public class PlainTaskPanel extends BaseTaskPanel { + /** + * Standard Constructor + */ + public PlainTaskPanel(Component aParent, boolean hasInfoArea, boolean hasStatusArea) + { + super(aParent); + + buildGuiArea(hasInfoArea, hasStatusArea); + setSize(450, getPreferredSize().height); + setTitle("Task Progress"); + + updateGui(); + } + + /** + * Simplified Constructor + */ public PlainTaskPanel(Component aParent) { this(aParent, true, true); } - public PlainTaskPanel(Component aParent, boolean hasInfoArea, boolean hasStatusArea) - { - super(aParent); - - buildGuiArea(hasInfoArea, hasStatusArea); - setSize(450, getPreferredSize().height); - setTitle("Task Progress"); - - updateGui(); - } - /** * Forms the actual GUI */ @@ -47,7 +64,7 @@ public class PlainTaskPanel extends BaseTaskPanel UnitProvider percentUP, timerUP; String colConstraints; Font aFont; - + colConstraints = "[][]"; if (hasStatusArea == true) colConstraints += "[]"; @@ -55,18 +72,18 @@ public class PlainTaskPanel extends BaseTaskPanel colConstraints += "[grow]"; setLayout(new MigLayout("", "[right][pref!][grow][right][pref!]", colConstraints)); aFont = (new JTextField()).getFont(); - + // Title area titleL = new JLabel("Title", JLabel.CENTER); add(titleL, "growx,span,wrap"); - + // Progress + Timer area - percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); + percentUP = new ConstUnitProvider(new NumberUnit("%", "%", 100.0, 2)); tmpL = new JLabel("Progress:"); progressL = new GLabel(percentUP, aFont, true); add(tmpL, ""); add(progressL, ""); - + timerUP = new ConstUnitProvider(new TimeCountUnit(2)); tmpL = new JLabel("Time:"); timerL = new GLabel(timerUP, aFont, true); @@ -81,30 +98,27 @@ public class PlainTaskPanel extends BaseTaskPanel add(tmpL, "newline"); add(statusL, "growx,span"); } - + // Info area infoTA = null; if (hasInfoArea == true) { - infoTA = GuiUtil.createUneditableTextArea(7, 0); -infoTA.setFont(new Font(aFont.getName(), Font.PLAIN, aFont.getSize()-2)); + infoTA = GuiUtil.createUneditableTextArea(7, 0); +infoTA.setFont(new Font(aFont.getName(), Font.PLAIN, aFont.getSize() - 2)); infoTA.setOpaque(true); - + tmpScrollPane = new JScrollPane(infoTA); add(tmpScrollPane, "growx,growy,newline,span"); } - - // Border - setBorder(new BevelBorder(BevelBorder.RAISED)); } - + @Override protected void updateGui() { // If progress >= 1.0, then we are done if (mProgress >= 1.0) isActive = false; - + progressL.setValue(mProgress); if (statusL != null) statusL.setValue(mStatus); diff --git a/src/glum/gui/table/BaseTableModel.java b/src/glum/gui/table/BaseTableModel.java new file mode 100644 index 0000000..b1fe7bc --- /dev/null +++ b/src/glum/gui/table/BaseTableModel.java @@ -0,0 +1,90 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.table.TableModel; + +/** + * Base class used to define a TableModel. This class provides for handling of each columns type and title. + * + * @author lopeznr1 + */ +public abstract class BaseTableModel implements TableModel +{ + // Attributes + private final Class[] classArr; + private final String[] nameArr; + + // State vars + private List listenerL; + + /** + * Standard Constructor + */ + public BaseTableModel(Class[] aClassArr, String[] aNameArr) + { + classArr = aClassArr; + nameArr = aNameArr; + if (classArr.length != nameArr.length) + throw new RuntimeException("Parameters aClassArr and aNameArr must be the same length!"); + + listenerL = new ArrayList<>(); + } + + @Override + public void addTableModelListener(TableModelListener aListener) + { + listenerL.add(aListener); + } + + @Override + public void removeTableModelListener(TableModelListener aListener) + { + listenerL.remove(aListener); + } + + @Override + public Class getColumnClass(int aColIndex) + { + return classArr[aColIndex]; + } + + @Override + public int getColumnCount() + { + return classArr.length; + } + + @Override + public String getColumnName(int aColIndex) + { + return nameArr[aColIndex]; + } + + /** + * Helper method to send out notification to the registered listeners. + */ + protected void notifyListeners() + { + TableModelEvent tmpEvent = new TableModelEvent(this); + for (TableModelListener aListener : listenerL) + aListener.tableChanged(tmpEvent); + } + +} diff --git a/src/glum/gui/table/ColorCellRenderer.java b/src/glum/gui/table/ColorCellRenderer.java new file mode 100644 index 0000000..c9fd813 --- /dev/null +++ b/src/glum/gui/table/ColorCellRenderer.java @@ -0,0 +1,74 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.awt.Color; +import java.awt.Component; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.border.Border; +import javax.swing.table.TableCellRenderer; + +/** + * Class that used to display a filled color rectangle for a table cell where the data model is a {@link Color} + */ +public class ColorCellRenderer extends JLabel implements TableCellRenderer +{ + // Constants + private static final long serialVersionUID = 1L; + + // State vars + private boolean showToolTips; + + // Cache vars + private Border cUnselectedBorder = null; + private Border cSelectedBorder = null; + + public ColorCellRenderer(boolean aShowToolTips) + { + showToolTips = aShowToolTips; + + setOpaque(true); // MUST do this for background to show up. + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object color, boolean isSelected, boolean hasFocus, + int row, int column) + { + Color newColor = (Color) color; + setBackground(newColor); + + if (isSelected) + { + if (cSelectedBorder == null) + cSelectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getSelectionBackground()); + setBorder(cSelectedBorder); + } + else + { + if (cUnselectedBorder == null) + cUnselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, table.getBackground()); + setBorder(cUnselectedBorder); + } + + String toolTipStr = null; + if (showToolTips == true && newColor != null) + toolTipStr = "RGB value: " + newColor.getRed() + ", " + newColor.getGreen() + ", " + newColor.getBlue(); + setToolTipText(toolTipStr); + + return this; + } +} diff --git a/src/glum/gui/table/ColorHSBLCellRenderer.java b/src/glum/gui/table/ColorHSBLCellRenderer.java new file mode 100644 index 0000000..479b725 --- /dev/null +++ b/src/glum/gui/table/ColorHSBLCellRenderer.java @@ -0,0 +1,76 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.awt.Color; +import java.awt.Component; + +import javax.swing.BorderFactory; +import javax.swing.JLabel; +import javax.swing.JTable; +import javax.swing.border.Border; +import javax.swing.table.TableCellRenderer; + +import glum.color.ColorHSBL; + +/** + * Class that used to display a filled color rectangle for a table cell where the data model is a {@link Color} + */ +public class ColorHSBLCellRenderer extends JLabel implements TableCellRenderer +{ + // Constants + private static final long serialVersionUID = 1L; + + // State vars + private boolean showToolTips; + + // Cache vars + private Border cUnselectedBorder = null; + private Border cSelectedBorder = null; + + public ColorHSBLCellRenderer(boolean aShowToolTips) + { + showToolTips = aShowToolTips; + + setOpaque(true); // MUST do this for background to show up. + } + + @Override + public Component getTableCellRendererComponent(JTable aTable, Object aObj, boolean isSelected, boolean hasFocus, + int row, int column) + { + Color tmpColor = ((ColorHSBL) aObj).getColor(); + setBackground(tmpColor); + + if (isSelected) + { + if (cSelectedBorder == null) + cSelectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, aTable.getSelectionBackground()); + setBorder(cSelectedBorder); + } + else + { + if (cUnselectedBorder == null) + cUnselectedBorder = BorderFactory.createMatteBorder(2, 5, 2, 5, aTable.getBackground()); + setBorder(cUnselectedBorder); + } + + String toolTipStr = null; + if (showToolTips == true && tmpColor != null) + toolTipStr = "RGB value: " + tmpColor.getRed() + ", " + tmpColor.getGreen() + ", " + tmpColor.getBlue(); + setToolTipText(toolTipStr); + + return this; + } +} diff --git a/src/glum/gui/table/KeyValueTableModel.java b/src/glum/gui/table/KeyValueTableModel.java index 6c51b33..583653f 100644 --- a/src/glum/gui/table/KeyValueTableModel.java +++ b/src/glum/gui/table/KeyValueTableModel.java @@ -1,36 +1,50 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.table; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + import javax.swing.table.AbstractTableModel; -import com.google.common.collect.Lists; - +/** + * Implementation of {@link AbstractTableModel} for displaying key / value pairs. + * + * @author lopeznr1 + */ public class KeyValueTableModel extends AbstractTableModel { - private String[] columnNames; - private List> myList; + private String[] columnNameArr; + private List> workL; /** - * Constructor + * Standard Constructor */ - public KeyValueTableModel() + public KeyValueTableModel(String aKeyHeader, String aValueHeader) { - this(null, null); - } + columnNameArr = new String[2]; + columnNameArr[0] = ""; + columnNameArr[1] = ""; - public KeyValueTableModel(String keyHeader, String valueHeader) - { - columnNames = new String[2]; - columnNames[0] = ""; - columnNames[1] = ""; + if (aKeyHeader != null) + columnNameArr[0] = aKeyHeader; - if (keyHeader != null) - columnNames[0] = keyHeader; + if (aValueHeader != null) + columnNameArr[1] = aValueHeader; - if (valueHeader != null) - columnNames[1] = valueHeader; - - myList = Lists.newArrayList(); + workL = new ArrayList<>(); } /** @@ -43,10 +57,10 @@ public class KeyValueTableModel extends AbstractTableModel if (aMap.isEmpty() == true) return; - startIndex = myList.size(); + startIndex = workL.size(); endIndex = startIndex + aMap.size(); - myList.addAll(aMap.entrySet()); + workL.addAll(aMap.entrySet()); fireTableRowsInserted(startIndex, endIndex); } @@ -58,11 +72,11 @@ public class KeyValueTableModel extends AbstractTableModel { int endIndex; - if (myList.isEmpty() == true) + if (workL.isEmpty() == true) return; - endIndex = myList.size() - 1; - myList.clear(); + endIndex = workL.size() - 1; + workL.clear(); fireTableRowsDeleted(0, endIndex); } @@ -70,44 +84,44 @@ public class KeyValueTableModel extends AbstractTableModel @Override public int getColumnCount() { - return columnNames.length; + return columnNameArr.length; } @Override public int getRowCount() { - return myList.size(); + return workL.size(); } @Override - public String getColumnName(int col) + public String getColumnName(int aCol) { - return columnNames[col]; + return columnNameArr[aCol]; } @Override - public Object getValueAt(int row, int col) + public Object getValueAt(int aRow, int aCol) { - if (row < 0 || row >= myList.size()) + if (aRow < 0 || aRow >= workL.size()) return null; // DataProviderShell Enabled - if (col == 0) - return myList.get(row).getKey(); - else if (col == 1) - return myList.get(row).getValue(); + if (aCol == 0) + return workL.get(aRow).getKey(); + else if (aCol == 1) + return workL.get(aRow).getValue(); return null; } @Override - public Class getColumnClass(int col) + public Class getColumnClass(int aCol) { return String.class; } @Override - public boolean isCellEditable(int row, int col) + public boolean isCellEditable(int aRow, int aCol) { // Note that the data/cell address is constant, // no matter where the cell appears on screen. diff --git a/src/glum/gui/table/NumberRenderer.java b/src/glum/gui/table/NumberRenderer.java new file mode 100644 index 0000000..e6a275d --- /dev/null +++ b/src/glum/gui/table/NumberRenderer.java @@ -0,0 +1,77 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.awt.Component; +import java.text.DecimalFormat; +import java.text.NumberFormat; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +/** + * TableCellRenderer used to display numerical values. + * + * @author lopeznr1 + */ +public class NumberRenderer extends DefaultTableCellRenderer +{ + // Constants + private static final long serialVersionUID = 1L; + + // Attributes + private final NumberFormat numberFormat; + private final String nanStr; + + /** + * Standard Constructor + * + * @param aPrependLabel: + * String that will be prepended to the table cell's value. + */ + public NumberRenderer(NumberFormat aNumberFormat, String aNanStr) + { + numberFormat = aNumberFormat; + nanStr = aNanStr; + } + + /** + * Simplified Constructor + * + * @param aNumberFmtStr + * The number string format to be sent to a DecimalFormat. + * @param aNanStr + * The String to be used as NaN. + */ + public NumberRenderer(String aNumberFmtStr, String aNanStr) + { + this(new DecimalFormat(aNumberFmtStr), aNanStr); + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) + { + String tmpStr = nanStr; + if (value instanceof Number) + { + double tmpVal = ((Number) value).doubleValue(); + if (Double.isNaN(tmpVal) == false) + tmpStr = numberFormat.format(value); + } + + return super.getTableCellRendererComponent(table, tmpStr, isSelected, hasFocus, row, column); + } + +} diff --git a/src/glum/gui/table/PrePendRenderer.java b/src/glum/gui/table/PrePendRenderer.java new file mode 100644 index 0000000..973d0b0 --- /dev/null +++ b/src/glum/gui/table/PrePendRenderer.java @@ -0,0 +1,53 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.awt.Component; + +import javax.swing.JTable; +import javax.swing.table.DefaultTableCellRenderer; + +/** + * TableCellRenderer used to display values prepended with a text string. + * + * @author lopeznr1 + */ +public class PrePendRenderer extends DefaultTableCellRenderer +{ + // Constants + private static final long serialVersionUID = 1L; + + // Attributes + private final String prependLabel; + + /** + * Standard Constructor + * + * @param aPrependLabel: + * String that will be prepended to the table cell's value. + */ + public PrePendRenderer(String aPrependLabel) + { + prependLabel = aPrependLabel; + } + + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) + { + String tmpStr = prependLabel + value; + return super.getTableCellRendererComponent(table, tmpStr, isSelected, hasFocus, row, column); + } + +} diff --git a/src/glum/gui/table/Scrolling.java b/src/glum/gui/table/Scrolling.java index 3432b98..af72384 100644 --- a/src/glum/gui/table/Scrolling.java +++ b/src/glum/gui/table/Scrolling.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. /* * Source from: http://www.chka.de/swing/table/JTableScrolling.java * diff --git a/src/glum/gui/table/SortDir.java b/src/glum/gui/table/SortDir.java new file mode 100644 index 0000000..330b2d8 --- /dev/null +++ b/src/glum/gui/table/SortDir.java @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +/** + * Enum which defines the possible sort states. + * + * @author lopeznr1 + */ +public enum SortDir +{ + NotSorted, + + Ascending, + + Descending, +} \ No newline at end of file diff --git a/src/glum/gui/table/TablePopupHandler.java b/src/glum/gui/table/TablePopupHandler.java new file mode 100644 index 0000000..34aebd7 --- /dev/null +++ b/src/glum/gui/table/TablePopupHandler.java @@ -0,0 +1,74 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.JPopupMenu; + +import glum.item.ItemManager; + +/** + * Object that provides the logic used to determine when a {@link JPopupMenu} should be displayed. + *

    + * In order for a popup menu to be shown at least one item must be selected in provided refManager. When a popup menu is + * triggered the current selection will not be changed. + * + * @author lopeznr1 + */ +public class TablePopupHandler extends MouseAdapter +{ + // Ref vars + private final ItemManager refManager; + private final JPopupMenu refPopupMenu; + + /** + * Standard Constructor + */ + public TablePopupHandler(ItemManager aManager, JPopupMenu aPopupMenu) + { + refManager = aManager; + refPopupMenu = aPopupMenu; + } + + @Override + public void mousePressed(MouseEvent aEvent) + { + maybeShowPopup(aEvent); + } + + @Override + public void mouseReleased(MouseEvent aEvent) + { + maybeShowPopup(aEvent); + } + + /** + * Helper method to handle the showing of the table popup menu. + */ + private void maybeShowPopup(MouseEvent aEvent) + { + // Bail if this is not a valid popup action + if (aEvent.isPopupTrigger() == false) + return; + + // Bail if no items are selected + if (refManager.getSelectedItems().isEmpty() == true) + return; + + refPopupMenu.show(aEvent.getComponent(), aEvent.getX(), aEvent.getY()); + } + +} diff --git a/src/glum/gui/table/TableSorter.java b/src/glum/gui/table/TableSorter.java index 83f99ea..c16af71 100644 --- a/src/glum/gui/table/TableSorter.java +++ b/src/glum/gui/table/TableSorter.java @@ -1,120 +1,363 @@ +/* + * Copyright (c) 1995, 2008, Oracle and/or its affiliates. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * - Neither the name of Oracle or the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package glum.gui.table; -import java.awt.*; +import java.awt.Component; import java.awt.event.*; import java.util.*; -import java.util.List; import javax.swing.*; import javax.swing.event.TableModelEvent; import javax.swing.event.TableModelListener; import javax.swing.table.*; +import com.google.common.collect.ImmutableList; + +import glum.gui.table.sort.DefaultSortIconProvider; +import glum.gui.table.sort.SortIconProvider; + /** - * TableSorter is a decorator for TableModels; adding sorting functionality to a - * supplied TableModel. TableSorter does not store or copy the data in its - * TableModel; instead it maintains a map from the row indexes of the view to - * the row indexes of the model. As requests are made of the sorter (like - * getValueAt(row, col)) they are passed to the underlying model after the row - * numbers have been translated via the internal mapping array. This way, the - * TableSorter appears to hold another copy of the table with the rows in a - * different order. - *

    - * TableSorter registers itself as a listener to the underlying model, just as - * the JTable itself would. Events recieved from the model are examined, - * sometimes manipulated (typically widened), and then passed on to the - * TableSorter's listeners (typically the JTable). If a change to the model has - * invalidated the order of TableSorter's rows, a note of this is made and the - * sorter will resort the rows the next time a value is requested. - *

    - * When the tableHeader property is set, either by using the setTableHeader() - * method or the two argument constructor, the table header may be used as a - * complete UI for TableSorter. The default renderer of the tableHeader is - * decorated with a renderer that indicates the sorting status of each column. - * In addition, a mouse listener is installed with the following behavior: + * {@link TableSorter} is a decorator for TableModels; adding sorting functionality to a supplied TableModel. + * TableSorter does not store or copy the data in its TableModel; instead it maintains a map from the row indexes of the + * view to the row indexes of the model. As requests are made of the sorter (like getValueAt(row, col)) they are passed + * to the underlying model after the row numbers have been translated via the internal mapping array. This way, the + * TableSorter appears to hold another copy of the table with the rows in a different order. + *

    + * TableSorter registers itself as a listener to the underlying model, just as the JTable itself would. Events received + * from the model are examined, sometimes manipulated (typically widened), and then passed on to the TableSorter's + * listeners (typically the JTable). If a change to the model has invalidated the order of TableSorter's rows, a note of + * this is made and the sorter will resort the rows the next time a value is requested. + *

    + * When the tableHeader property is set, either by using the setTableHeader() method or the two argument constructor, + * the table header may be used as a complete UI for TableSorter. The default renderer of the tableHeader is decorated + * with a renderer that indicates the sorting status of each column. In addition, a mouse listener is installed with the + * following behavior: *

      - *
    • - * Mouse-click: Clears the sorting status of all other columns and advances the - * sorting status of that column through three values: {NOT_SORTED, ASCENDING, - * DESCENDING} (then back to NOT_SORTED again). - *
    • - * SHIFT-mouse-click: Clears the sorting status of all other columns and cycles - * the sorting status of the column through the same three values, in the - * opposite order: {NOT_SORTED, DESCENDING, ASCENDING}. - *
    • - * CONTROL-mouse-click and CONTROL-SHIFT-mouse-click: as above except that the - * changes to the column do not cancel the statuses of columns that are already - * sorting - giving a way to initiate a compound sort. + *
    • Mouse-Click: Clears the sorting status of all other columns and sets the current column as the primary sort + * column. + *
    • CONTROL-Mouse-Click: Adds a secondary (or lower rank) column to sort upon. + *
    • SHIFT-Mouse-Click: Clears the sort status of the selected column. The column will no longer influence the sort. *
    - *

    - * This is a long overdue rewrite of a class of the same name that first - * appeared in the swing table demos in 1997. - * + * Please note that mouse-clicking on the columns will toggle the sort directive of that column from ascending to + * descending. + *

    + * Column headers that have been selected for sorting provide the following (default) visual indicators: + *

      + *
    • The 1st (primary) sort column is displayed with a bright red icon. + *
    • The 2nd sort column is displayed with a dark red icon. + *
    • The 3rd sort column is displayed with a black icon. + *
    • The 4th (and all lower level) sort column is displayed with a gray icon. + *
    + * Please note that the icons used for painting the sort indicators can be customized via the method + * {@link #setSortIconProvider(SortIconProvider)}. + *

    + * This class is an incompatible rewrite of the TableSorter class (~2004Feb27). The original class can be currently be + * sourced (as of date 2019Aug20) from:
    + * https://docs.oracle.com/javase/tutorial/uiswing/examples/components/TableSorterDemoProject/src/components/TableSorter.java
    + * or
    + * https://web.archive.org/web/20171019235101/http://docs.oracle.com/javase/tutorial/uiswing/examples/components/TableSorterDemoProject/src/components/TableSorter.java + * * @author Philip Milne * @author Brendon McLean * @author Dan van Enckevort * @author Parwinder Sekhon - * @version 2.0 02/27/04 + * @author lopeznr1 (Rewrite author as used in Glum library) */ -@SuppressWarnings("unchecked") -// This class is no longer supported. It should be removed as of Java 1.7 public class TableSorter extends AbstractTableModel { - protected TableModel tableModel; - // Constants - private static final long serialVersionUID = -1999L; - public static final int DESCENDING = -1; - public static final int NOT_SORTED = 0; - public static final int ASCENDING = 1; + private static final Comparator LEXICAL_COMPARATOR = Comparator.comparing(Object::toString); - private static Directive EMPTY_DIRECTIVE = new Directive(-1, NOT_SORTED); - - public static final Comparator COMPARABLE_COMAPRATOR = new Comparator() - { - @Override - public int compare(Object o1, Object o2) - { - return ((Comparable)o1).compareTo(o2); - } - }; - public static final Comparator LEXICAL_COMPARATOR = new Comparator() - { - @Override - public int compare(Object o1, Object o2) - { - return o1.toString().compareTo(o2.toString()); - } - }; + // Ref vars + private final TableModel refTableModel; + private JTableHeader refTableHeader; + // State vars private Row[] viewToModel; private int[] modelToView; - private JTableHeader tableHeader; - private MouseListener mouseListener; - private TableModelListener tableModelListener; - private Map columnComparators = new HashMap(); - private List sortingColumns = new ArrayList(); + private final MouseListener mouseListener; + private final TableModelListener tableModelListener; + private final Map, Comparator> columnComparatorClassM; + private final Map> columnComparatorIndexM; + private final List sortingColumnL; private boolean isSortEnabled; - public TableSorter() + // Render vars + private SortIconProvider refSortIconProvider; + private ImmutableList sortIconAsceL; + private ImmutableList sortIconDescL; + private int cSize; + + /** + * Standard Constructor + * + * @param aTableModel + * The table model which will be used as the backing table model. + * @param aTableHeader + * The table header asociated with the JTable. + */ + public TableSorter(TableModel aTableModel, JTableHeader aTableHeader) { - this.mouseListener = new MouseHandler(); - this.tableModelListener = new TableModelHandler(); + refTableModel = aTableModel; + + viewToModel = null; + modelToView = null; + + mouseListener = new MouseHandler(); + tableModelListener = new TableModelHandler(); + columnComparatorClassM = new HashMap<>(); + columnComparatorIndexM = new HashMap<>(); + sortingColumnL = new ArrayList<>(); isSortEnabled = true; + + refSortIconProvider = DefaultSortIconProvider.Default; + sortIconAsceL = ImmutableList.of(); + sortIconDescL = ImmutableList.of(); + cSize = -1; + + if (aTableHeader != null) + setTableHeader(aTableHeader); + + // Register for events of interest + refTableModel.addTableModelListener(tableModelListener); + fireTableStructureChanged(); } - public TableSorter(TableModel tableModel) + /** Simplified Constructor */ + public TableSorter(TableModel aTableModel) { - this(); - setTableModel(tableModel); + this(aTableModel, null); } - public TableSorter(TableModel tableModel, JTableHeader tableHeader) + /** + * Returns the {@link SortDir} of the specified column. + */ + public SortDir getSortDir(int aColNum) { - this(); - setTableHeader(tableHeader); - setTableModel(tableModel); + var tmpDirective = getDirective(aColNum); + if (tmpDirective != null) + return tmpDirective.sortDir; + + return SortDir.NotSorted; + } + + /** + * Returns an ordered map of column model index to the {@link SortDir}. + *

    + * The returned map is a snapshot of the columns that are sorted and their ordered priority. + */ + public Map getSortState() + { + var retSortStateM = new LinkedHashMap(); + for (var aDirective : sortingColumnL) + retSortStateM.put(aDirective.column, aDirective.sortDir); + + return retSortStateM; + } + + public JTableHeader getTableHeader() + { + return refTableHeader; + } + + public TableModel getTableModel() + { + return refTableModel; + } + + /** + * Returns true if any columns have a sort directive set. + */ + public boolean isSorting() + { + return sortingColumnL.size() != 0; + } + + /** + * Sets in the Comparator that will be associated with columns of data type, aType. + *

    + * This Comparator will only be used if the column does not have a specific Comparator associated with it. + * + * @param aType + * @param aComparator + */ + public void setColumnClassComparator(Class aType, Comparator aComparator) + { + if (aComparator == null) + columnComparatorClassM.remove(aType); + else + columnComparatorClassM.put(aType, aComparator); + } + + /** + * Sets in the Comparator that will be associated with the specified column. + *

    + * This Comparator will be used before other (criteria matching) Comparators. + * + * @param aColNum + * @param aComparator + */ + public void setColumnIndexComparator(int aColNum, Comparator aComparator) + { + if (aComparator == null) + columnComparatorIndexM.remove(aColNum); + else + columnComparatorIndexM.put(aColNum, aComparator); + } + + /** + * Sets the {@link SortIconProvider}. + */ + public void setSortIconProvider(SortIconProvider aSortIconProvider) + { + refSortIconProvider = aSortIconProvider; + + // Invalidate the sort icons cache + sortIconAsceL = ImmutableList.of(); + sortIconDescL = ImmutableList.of(); + cSize = -1; + + sortingStatusChanged(); + } + + /** + * Installs an ordered map of column model index to the {@link SortDir}. + *

    + * The installed map will set the columns that are sorted and their ordered sort priority. + */ + public void setSortState(Map aSortDirM) + { + sortingColumnL.clear(); + for (var aCol : aSortDirM.keySet()) + sortingColumnL.add(new Directive(aCol, aSortDirM.get(aCol))); + + sortingStatusChanged(); + } + + /** + * Sets whether sorting is enabled or disabled. + */ + public void setSortingEnabled(boolean aBool) + { + isSortEnabled = aBool; + + clearSortingState(); + } + + /** + * Sets in the table header associated with the backing TableModel. A mouse listener will be installed that provides + * a fully functional UI for configuring the rank and sort directive of the various columns. + * + * @param aTableHeader + */ + public void setTableHeader(JTableHeader aTableHeader) + { + if (refTableHeader != null) + { + refTableHeader.removeMouseListener(mouseListener); + TableCellRenderer defaultRenderer = refTableHeader.getDefaultRenderer(); + if (defaultRenderer instanceof SortableHeaderRenderer) + { + refTableHeader.setDefaultRenderer(((SortableHeaderRenderer) defaultRenderer).tableCellRenderer); + } + } + refTableHeader = aTableHeader; + if (refTableHeader != null) + { + refTableHeader.addMouseListener(mouseListener); + refTableHeader.setDefaultRenderer(new SortableHeaderRenderer(refTableHeader.getDefaultRenderer())); + } + } + + public int modelIndex(int viewIndex) + { + return getViewToModel()[viewIndex].modelIndex; + } + + public int viewIndex(int modelIndex) + { + return getModelToView()[modelIndex]; + } + + // TableModel interface methods + @Override + public int getRowCount() + { + return (refTableModel == null) ? 0 : refTableModel.getRowCount(); + } + + @Override + public int getColumnCount() + { + return (refTableModel == null) ? 0 : refTableModel.getColumnCount(); + } + + @Override + public String getColumnName(int column) + { + return refTableModel.getColumnName(column); + } + + @Override + public Class getColumnClass(int column) + { + return refTableModel.getColumnClass(column); + } + + @Override + public boolean isCellEditable(int row, int column) + { + return refTableModel.isCellEditable(modelIndex(row), column); + } + + @Override + public Object getValueAt(int row, int column) + { + return refTableModel.getValueAt(modelIndex(row), column); + } + + @Override + public void setValueAt(Object aValue, int row, int column) + { + refTableModel.setValueAt(aValue, modelIndex(row), column); + } + + /** + * Helper method that clears all sorting directives and sends out the proper notification. + */ + private void cancelSorting() + { + sortingColumnL.clear(); + sortingStatusChanged(); } private void clearSortingState() @@ -123,166 +366,87 @@ public class TableSorter extends AbstractTableModel modelToView = null; } - public TableModel getTableModel() + /** + * Helper method that returns the appropriate Comparator for the specified column. + */ + private Comparator getComparator(int aColNum) { - return tableModel; - } + Comparator retComparator; - public void setTableModel(TableModel tableModel) - { - if (this.tableModel != null) - { - this.tableModel.removeTableModelListener(tableModelListener); - } + // Utilize the column index Comparator (if specified) + retComparator = columnComparatorIndexM.get(aColNum); + if (retComparator != null) + return retComparator; - this.tableModel = tableModel; - if (this.tableModel != null) - { - this.tableModel.addTableModelListener(tableModelListener); - } + // Utilize the class type Comparator (if specified) + Class columnType = refTableModel.getColumnClass(aColNum); + retComparator = columnComparatorClassM.get(columnType); + if (retComparator != null) + return retComparator; - clearSortingState(); - fireTableStructureChanged(); - } - - public JTableHeader getTableHeader() - { - return tableHeader; - } - - public void setTableHeader(JTableHeader tableHeader) - { - if (this.tableHeader != null) - { - this.tableHeader.removeMouseListener(mouseListener); - TableCellRenderer defaultRenderer = this.tableHeader.getDefaultRenderer(); - if (defaultRenderer instanceof SortableHeaderRenderer) - { - this.tableHeader.setDefaultRenderer(((SortableHeaderRenderer)defaultRenderer).tableCellRenderer); - } - } - this.tableHeader = tableHeader; - if (this.tableHeader != null) - { - this.tableHeader.addMouseListener(mouseListener); - this.tableHeader.setDefaultRenderer(new SortableHeaderRenderer(this.tableHeader.getDefaultRenderer())); - } - } - - public boolean isSorting() - { - return sortingColumns.size() != 0; - } - - private Directive getDirective(int column) - { - for (int i = 0; i < sortingColumns.size(); i++) - { - Directive directive = (Directive)sortingColumns.get(i); - if (directive.column == column) - { - return directive; - } - } - return EMPTY_DIRECTIVE; - } - - public int getSortingStatus(int column) - { - return getDirective(column).direction; - } - - private void sortingStatusChanged() - { - clearSortingState(); - fireTableDataChanged(); - if (tableHeader != null) - { - tableHeader.repaint(); - } - } - - public void setSortingStatus(int column, int status) - { - Directive directive = getDirective(column); - if (directive != EMPTY_DIRECTIVE) - { - sortingColumns.remove(directive); - } - if (status != NOT_SORTED) - { - sortingColumns.add(new Directive(column, status)); - } - sortingStatusChanged(); - } - - protected Icon getHeaderRendererIcon(int column, int size) - { - Directive directive = getDirective(column); - if (directive == EMPTY_DIRECTIVE) - { - return null; - } - // return new Arrow(directive.direction == DESCENDING, size, - // sortingColumns.indexOf(directive)); - return null; - } - - private void cancelSorting() - { - sortingColumns.clear(); - sortingStatusChanged(); - } - - public void setColumnComparator(Class type, Comparator comparator) - { - if (comparator == null) - { - columnComparators.remove(type); - } - else - { - columnComparators.put(type, comparator); - } - } - - protected Comparator getComparator(int column) - { - Class columnType = tableModel.getColumnClass(column); - Comparator comparator = (Comparator)columnComparators.get(columnType); - if (comparator != null) - { - return comparator; - } if (Comparable.class.isAssignableFrom(columnType)) - { - return COMPARABLE_COMAPRATOR; - } + return Comparator.naturalOrder(); + return LEXICAL_COMPARATOR; } - private Row[] getViewToModel() + /** + * Helper method that returns the sort directive of the specified column. + *

    + * Returns null if the column is not sorted. + */ + private Directive getDirective(int aColNum) { - if (viewToModel == null) + for (Directive aDirective : sortingColumnL) { - int tableModelRowCount = tableModel.getRowCount(); - viewToModel = new Row[tableModelRowCount]; - for (int row = 0; row < tableModelRowCount; row++) - { - viewToModel[row] = new Row(row); - } - - if (isSorting()) - { - Arrays.sort(viewToModel); - } + if (aDirective.column == aColNum) + return aDirective; } - return viewToModel; + return null; } - public int modelIndex(int viewIndex) + /** + * Helper method that returns the icon that should be used for the specified column. + *

    + * If the column is not sorted then null will be returned. + * + * @param aColumn + * @param aSize + * @return + */ + private Icon getHeaderRendererIcon(int aColumn, int aSize) { - return getViewToModel()[viewIndex].modelIndex; + // Bail if this column is not sorted + Directive directive = getDirective(aColumn); + if (directive == null) + return null; + + // Synthesize new icons if the size differs from the cache + if (aSize != cSize) + { + sortIconAsceL = ImmutableList.copyOf(refSortIconProvider.getIconsForSortAsce(aSize)); + sortIconDescL = ImmutableList.copyOf(refSortIconProvider.getIconsForSortDesc(aSize)); + } + cSize = aSize; + + // Locate the proper sort icon list + List sortIconL = sortIconAsceL; + if (directive.sortDir == SortDir.Descending) + sortIconL = sortIconDescL; + + // Bail if there are no available sort icons + if (sortIconL.size() == 0) + return null; + + // Retrieve the priority + int priority = sortingColumnL.indexOf(directive); + if (priority < 0) + priority = 0; + else if (priority >= sortIconL.size()) + priority = sortIconL.size() - 1; + + // Return the proper icon corresponding to the priority + return sortIconL.get(priority); } private int[] getModelToView() @@ -299,111 +463,94 @@ public class TableSorter extends AbstractTableModel return modelToView; } - public int viewIndex(int modelIndex) + private Row[] getViewToModel() { - return getModelToView()[modelIndex]; + if (viewToModel == null) + { + int tableModelRowCount = refTableModel.getRowCount(); + viewToModel = new Row[tableModelRowCount]; + for (int row = 0; row < tableModelRowCount; row++) + { + viewToModel[row] = new Row(row); + } + + if (isSorting()) + { + Arrays.sort(viewToModel); + } + } + return viewToModel; } - // TableModel interface methods - - @Override - public int getRowCount() + /** + * Sets the {@link SortDir} for a specific column. + *

    + * Note that this column will be sorted after any previously sorted column. + */ + private void setSortDir(int aColNum, SortDir aSortDir) { - return (tableModel == null) ? 0 : tableModel.getRowCount(); + var directive = getDirective(aColNum); + if (directive != null) + sortingColumnL.remove(directive); + + if (aSortDir != SortDir.NotSorted) + sortingColumnL.add(new Directive(aColNum, aSortDir)); + + sortingStatusChanged(); } - @Override - public int getColumnCount() + /** + * Helper method that is triggered whenever any of the sort state changes. + *

    + * Event notification will be sent out. + */ + private void sortingStatusChanged() { - return (tableModel == null) ? 0 : tableModel.getColumnCount(); - } - - @Override - public String getColumnName(int column) - { - return tableModel.getColumnName(column); - } - - @Override - public Class getColumnClass(int column) - { - return tableModel.getColumnClass(column); - } - - @Override - public boolean isCellEditable(int row, int column) - { - return tableModel.isCellEditable(modelIndex(row), column); - } - - @Override - public Object getValueAt(int row, int column) - { - return tableModel.getValueAt(modelIndex(row), column); - } - - @Override - public void setValueAt(Object aValue, int row, int column) - { - tableModel.setValueAt(aValue, modelIndex(row), column); - } - - - public void setSortingEnabled(boolean aBool) - { - isSortEnabled = aBool; - clearSortingState(); + fireTableDataChanged(); + if (refTableHeader != null) + refTableHeader.repaint(); } - - // Helper classes - private class Row implements Comparable + private class Row implements Comparable { - private int modelIndex; + // Attributes + private final int modelIndex; - public Row(int index) + /** Standard Constructor */ + public Row(int aIndex) { - this.modelIndex = index; + modelIndex = aIndex; } @Override - public int compareTo(Object o) + @SuppressWarnings("unchecked") + public int compareTo(Row aRow) { int row1 = modelIndex; - int row2 = ((Row)o).modelIndex; + int row2 = aRow.modelIndex; - for (Iterator it = sortingColumns.iterator(); it.hasNext();) + for (Directive aDirective : sortingColumnL) { - Directive directive = (Directive)it.next(); - int column = directive.column; - Object o1 = tableModel.getValueAt(row1, column); - Object o2 = tableModel.getValueAt(row2, column); + int column = aDirective.column; + Object o1 = refTableModel.getValueAt(row1, column); + Object o2 = refTableModel.getValueAt(row2, column); int comparison = 0; // Define null less than everything, except null. if (o1 == null && o2 == null) - { comparison = 0; - } else if (o1 == null) - { comparison = -1; - } else if (o2 == null) - { comparison = 1; - } else - { - comparison = getComparator(column).compare(o1, o2); - } + comparison = ((Comparator) getComparator(column)).compare(o1, o2); + if (comparison != 0) - { - return directive.direction == DESCENDING ? -comparison : comparison; - } + return aDirective.sortDir == SortDir.Descending ? -comparison : comparison; } return 0; } @@ -455,7 +602,8 @@ public class TableSorter extends AbstractTableModel // which can be a performance problem for large tables. The last // clause avoids this problem. int column = e.getColumn(); - if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS && getSortingStatus(column) == NOT_SORTED && modelToView != null) + if (e.getFirstRow() == e.getLastRow() && column != TableModelEvent.ALL_COLUMNS + && getSortDir(column) == SortDir.NotSorted && modelToView != null) { int viewIndex = getModelToView()[e.getFirstRow()]; fireTableChanged(new TableModelEvent(TableSorter.this, viewIndex, viewIndex, column, e.getType())); @@ -473,145 +621,76 @@ public class TableSorter extends AbstractTableModel private class MouseHandler extends MouseAdapter { @Override - public void mouseClicked(MouseEvent e) + public void mouseClicked(MouseEvent aEvent) { - JTableHeader h = (JTableHeader)e.getSource(); - TableColumnModel columnModel = h.getColumnModel(); - int viewColumn = columnModel.getColumnIndexAtX(e.getX()); - int column = columnModel.getColumn(viewColumn).getModelIndex(); - // Bail if sorting is disabled if (isSortEnabled == false) return; - - if (column != -1) - { - int status = getSortingStatus(column); - if (!e.isControlDown()) - { - cancelSorting(); - } - // // Cycle the sorting states through {NOT_SORTED, ASCENDING, - // DESCENDING} or - // // {NOT_SORTED, DESCENDING, ASCENDING} depending on whether shift - // is pressed. - // status = status + (e.isShiftDown() ? -1 : 1); - // status = (status + 4) % 3 - 1; // signed mod, returning {-1, 0, - // 1} - if (status == ASCENDING) - status = DESCENDING; - else - status = ASCENDING; - setSortingStatus(column, status); - } - } - } + // Bail if no view column + JTableHeader h = (JTableHeader) aEvent.getSource(); + TableColumnModel columnModel = h.getColumnModel(); + int viewColumn = columnModel.getColumnIndexAtX(aEvent.getX()); + if (viewColumn == -1) + return; - private static class Arrow implements Icon - { - private boolean descending; - private int size; - private int priority; + // Bail if no model column + int column = columnModel.getColumn(viewColumn).getModelIndex(); + if (column == -1) + return; - public Arrow(boolean descending, int size, int priority) - { - this.descending = descending; - this.size = size; - this.priority = priority; - } + // Save off the current sort direction + var prevSortDir = getSortDir(column); - @Override - public void paintIcon(Component c, Graphics g, int x, int y) - { - Color color = c == null ? Color.GRAY : c.getBackground(); - // In a compound sort, make each succesive triangle 20% - // smaller than the previous one. - int dx = (int)(size / 2 * Math.pow(0.8, priority)); - int dy = descending ? dx : -dx; - // Align icon (roughly) with font baseline. - y = y + 5 * size / 6 + (descending ? -dy : 0); - int shift = descending ? 1 : -1; - g.translate(x, y); + // Clear all sort status if CTRL or SHIFT are not pressed + if (aEvent.isControlDown() == false && aEvent.isShiftDown() == false) + cancelSorting(); - // Right diagonal. - g.setColor(color.darker()); - g.drawLine(dx / 2, dy, 0, 0); - g.drawLine(dx / 2, dy + shift, 0, shift); + // Alternate between Ascending, Descending sorting + var nextSortDir = SortDir.Ascending; + if (prevSortDir == SortDir.Ascending) + nextSortDir = SortDir.Descending; - // Left diagonal. - g.setColor(color.brighter()); - g.drawLine(dx / 2, dy, dx, 0); - g.drawLine(dx / 2, dy + shift, dx, shift); + // If SHIFT is pressed then clear the sorting status + if (aEvent.isShiftDown() == true) + nextSortDir = SortDir.NotSorted; - // Horizontal line. - if (descending) - { - g.setColor(color.darker().darker()); - } - else - { - g.setColor(color.brighter().brighter()); - } - g.drawLine(dx, 0, 0, 0); - - g.setColor(color); - g.translate(-x, -y); - } - - @Override - public int getIconWidth() - { - return size; - } - - @Override - public int getIconHeight() - { - return size; + setSortDir(column, nextSortDir); } } private class SortableHeaderRenderer implements TableCellRenderer { - private TableCellRenderer tableCellRenderer; + // Attributes + private final TableCellRenderer tableCellRenderer; - public SortableHeaderRenderer(TableCellRenderer tableCellRenderer) + /** Standard Constructor */ + public SortableHeaderRenderer(TableCellRenderer aTableCellRenderer) { - this.tableCellRenderer = tableCellRenderer; + tableCellRenderer = aTableCellRenderer; } @Override - public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, + int row, int column) { - Component c = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if (c instanceof JLabel) + var tmpComp = tableCellRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); + if (tmpComp instanceof JLabel aLabel) { - JLabel l = (JLabel)c; - l.setHorizontalTextPosition(JLabel.LEFT); + aLabel.setHorizontalTextPosition(JLabel.LEFT); int modelColumn = table.convertColumnIndexToModel(column); - l.setIcon(getHeaderRendererIcon(modelColumn, l.getFont().getSize())); + aLabel.setIcon(getHeaderRendererIcon(modelColumn, aLabel.getFont().getSize())); } - return c; + return tmpComp; } - /* - * public void setEnabled(boolean aBool) { - * getTableCellRendererComponent.setEnabled(aBool); - * - * } - */ } - private static class Directive + /** + * Record that holds the state of a specific column and it's associated sort direction. + */ + private static record Directive(int column, SortDir sortDir) { - private int column; - private int direction; - - public Directive(int column, int direction) - { - this.column = column; - this.direction = direction; - } } + } diff --git a/src/glum/gui/table/sort/DefaultSortIconProvider.java b/src/glum/gui/table/sort/DefaultSortIconProvider.java new file mode 100644 index 0000000..56d9327 --- /dev/null +++ b/src/glum/gui/table/sort/DefaultSortIconProvider.java @@ -0,0 +1,75 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table.sort; + +import java.awt.Color; +import java.util.*; + +import javax.swing.Icon; + +import com.google.common.collect.ImmutableList; + +/** + * Default implementation of the SortIconProvider interface. + *

    + * This SortIconProvider provides a flexible number of "priorities" based on the list of colors provided at construction + * time. + * + * @author lopeznr1 + */ +public class DefaultSortIconProvider implements SortIconProvider +{ + // Constants + private static final List DefaultColorL = ImmutableList.of(Color.RED, Color.RED.darker(), Color.BLACK, + Color.LIGHT_GRAY); + public static final DefaultSortIconProvider Default = new DefaultSortIconProvider(DefaultColorL); + + // Attributes + private final ImmutableList refColorL; + + /** + * Standard Constructor + * + * @param aColorL + * The list of colors for each priority. Colors at the front of the list are associated with a higher + * priority. + */ + public DefaultSortIconProvider(List aColorL) + { + refColorL = ImmutableList.copyOf(aColorL); + } + + @Override + public List getIconsForSortAsce(int aSize) + { + List retL = new ArrayList<>(); + + for (Color aColor : refColorL) + retL.add(new SortArrow(false, aSize, aColor)); + + return retL; + } + + @Override + public List getIconsForSortDesc(int aSize) + { + List retL = new ArrayList<>(); + + for (Color aColor : refColorL) + retL.add(new SortArrow(true, aSize, aColor)); + + return retL; + } + +} diff --git a/src/glum/gui/table/sort/SortArrow.java b/src/glum/gui/table/sort/SortArrow.java new file mode 100644 index 0000000..2fa76b9 --- /dev/null +++ b/src/glum/gui/table/sort/SortArrow.java @@ -0,0 +1,100 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table.sort; + +import java.awt.*; + +import javax.swing.Icon; + +/** + * Icon used to render the sort indicator. + * + * @author lopeznr1 + */ +public class SortArrow implements Icon +{ + // Attributes + private final boolean descending; + private final int size; + private final Color fillColor; + + /** + * Standard Constructor + * + * @param aDescending + * @param aSize + * @param aPriority + */ + public SortArrow(boolean aDescending, int aSize, Color aColor) + { + descending = aDescending; + size = aSize; + fillColor = aColor; + } + + @Override + public void paintIcon(Component aComp, Graphics g, int x, int y) + { + int dx = (int) (size / 2.0); + int dy = descending ? dx : -dx; + + // Align icon (roughly) with font baseline. + y = y + 5 * size / 6 + (descending ? -dy : 0); + int shift = descending ? 1 : -1; + g.translate(x, y); + + int[] xArr = { dx / 2, 0, dx }; + int[] yArr = { dy + shift, shift, 0 }; + + // Draw the solid area + g.setColor(fillColor); + g.fillPolygon(xArr, yArr, 3); + + // Draw the border area + Color tmpColor = aComp == null ? Color.GRAY : aComp.getBackground(); + + // Right diagonal. + g.setColor(tmpColor.darker()); + g.drawLine(dx / 2, dy, 0, 0); + g.drawLine(dx / 2, dy + shift, 0, shift); + + // Left diagonal. + g.setColor(tmpColor.brighter()); + g.drawLine(dx / 2, dy, dx, 0); + g.drawLine(dx / 2, dy + shift, dx, shift); + + // Horizontal line. + if (descending) + g.setColor(tmpColor.darker().darker()); + else + g.setColor(tmpColor.brighter().brighter()); + g.drawLine(dx, 0, 0, 0); + + g.setColor(tmpColor); + g.translate(-x, -y); + } + + @Override + public int getIconWidth() + { + return size; + } + + @Override + public int getIconHeight() + { + return size; + } + +} diff --git a/src/glum/gui/table/sort/SortArrowLegacy.java b/src/glum/gui/table/sort/SortArrowLegacy.java new file mode 100644 index 0000000..da0dacc --- /dev/null +++ b/src/glum/gui/table/sort/SortArrowLegacy.java @@ -0,0 +1,95 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table.sort; + +import java.awt.*; + +import javax.swing.Icon; + +/** + * Legacy Icon used to render the sort indicator. + * + * @author lopeznr1 + */ +class SortArrowLegacy implements Icon +{ + // Attributes + private final boolean descending; + private final int size; + private final int priority; + + /** + * Standard Constructor + * + * @param aDescending + * @param aSize + * @param aPriority + */ + public SortArrowLegacy(boolean aDescending, int aSize, int aPriority) + { + descending = aDescending; + size = aSize; + priority = aPriority; + } + + @Override + public void paintIcon(Component aComp, Graphics g, int x, int y) + { + Color color = aComp == null ? Color.GRAY : aComp.getBackground(); + // In a compound sort, make each succesive triangle 20% + // smaller than the previous one. + int dx = (int) (size / 2 * Math.pow(0.8, priority)); + int dy = descending ? dx : -dx; + // Align icon (roughly) with font baseline. + y = y + 5 * size / 6 + (descending ? -dy : 0); + int shift = descending ? 1 : -1; + g.translate(x, y); + + // Right diagonal. + g.setColor(color.darker()); + g.drawLine(dx / 2, dy, 0, 0); + g.drawLine(dx / 2, dy + shift, 0, shift); + + // Left diagonal. + g.setColor(color.brighter()); + g.drawLine(dx / 2, dy, dx, 0); + g.drawLine(dx / 2, dy + shift, dx, shift); + + // Horizontal line. + if (descending) + { + g.setColor(color.darker().darker()); + } + else + { + g.setColor(color.brighter().brighter()); + } + g.drawLine(dx, 0, 0, 0); + + g.setColor(color); + g.translate(-x, -y); + } + + @Override + public int getIconWidth() + { + return size; + } + + @Override + public int getIconHeight() + { + return size; + } +} \ No newline at end of file diff --git a/src/glum/gui/table/sort/SortIconProvider.java b/src/glum/gui/table/sort/SortIconProvider.java new file mode 100644 index 0000000..75ee924 --- /dev/null +++ b/src/glum/gui/table/sort/SortIconProvider.java @@ -0,0 +1,46 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.gui.table.sort; + +import java.util.List; + +import javax.swing.Icon; + +/** + * Interface that allows customization of the sort icons. + * + * @author lopeznr1 + */ +public interface SortIconProvider +{ + /** + * Returns a list of icons that will be utilized as the sort icon indicators (when the items are sorted ascending). + * Icons at the front of the list will be associated with columns of data that have a higher sort priority. + * + * @param aSize + * The size of the sort icons in pixels. + * @return + */ + public List getIconsForSortAsce(int aSize); + + /** + * Returns a list of icons that will be utilized as the sort icon indicators (when the items are sorted descending). + * Icons at the front of the list will be associated with columns of data that have a higher sort priority. + * + * @param aSize + * The size of the sort icons in pixels. + * @return + */ + public List getIconsForSortDesc(int aSize); +} diff --git a/src/glum/gui/unit/DateUnitPanel.java b/src/glum/gui/unit/DateUnitPanel.java index d88e756..2c76c19 100644 --- a/src/glum/gui/unit/DateUnitPanel.java +++ b/src/glum/gui/unit/DateUnitPanel.java @@ -1,39 +1,48 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import java.awt.Dimension; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.List; -import java.util.TimeZone; +import java.awt.event.*; +import java.util.*; -import javax.swing.*; +import javax.swing.JLabel; +import javax.swing.JRadioButton; import javax.swing.table.DefaultTableCellRenderer; -import com.google.common.collect.Lists; - import glum.database.QueryItem; import glum.gui.GuiUtil; -import glum.gui.component.GComboBox; -import glum.gui.component.GLabel; -import glum.gui.component.GTextField; +import glum.gui.component.*; import glum.gui.document.CharDocument; -import glum.gui.panel.itemList.ItemHandler; import glum.gui.panel.itemList.ItemListPanel; import glum.gui.panel.itemList.StaticItemProcessor; import glum.gui.panel.itemList.query.QueryComposer; import glum.gui.panel.itemList.query.QueryItemHandler; -import glum.unit.DateUnit; -import glum.unit.DateUnitProvider; -import glum.unit.UnitProvider; - +import glum.unit.*; import net.miginfocom.swing.MigLayout; -public class DateUnitPanel extends EditorPanel implements ActionListener +/** + * Panel that allows the user to view and configure a {@link DateUnitProvider}. + * + * @author lopeznr1 + */ +public class DateUnitPanel extends EditorPanel implements ActionListener, ItemListener { // Constants // @formatter:off public static final String[][] DescriptionArr = { - {"G", "Era", "AD"}, + {"G", "Era", "AD"}, {"y", "Year", "1996"}, {"M", "Month in year", "Jul; 07"}, {"w", "Week in year", "27"}, @@ -56,7 +65,7 @@ public class DateUnitPanel extends EditorPanel implements ActionListener private GComboBox protoBox; private GTextField custTF; private GLabel exampleL; - private ItemListPanel ruleLP; + private ItemListPanel ruleLP; private DefaultTableCellRenderer plainRenderer; private JLabel timeZoneL; private GComboBox timeZoneBox; @@ -64,10 +73,9 @@ public class DateUnitPanel extends EditorPanel implements ActionListener // State vars private DateUnitProvider myUnitProvider; + /** Standard Constructor */ public DateUnitPanel() { - super(); - myUnitProvider = null; buildGuiArea(); @@ -93,29 +101,34 @@ public class DateUnitPanel extends EditorPanel implements ActionListener notifyListeners(this, ID_UPDATE, "unit.update"); } + @Override + public void itemStateChanged(ItemEvent aEvent) + { + updateModel(); + updateGui(); + + notifyListeners(this, ID_UPDATE, "unit.update"); + } + @Override public void setUnitProvider(UnitProvider aUnitProvider) { - List protoNameList; - DateUnit activeUnit; - TimeZone activeTimeZone; - // Update our UnitProvider myUnitProvider = null; if (aUnitProvider instanceof DateUnitProvider) - myUnitProvider = (DateUnitProvider)aUnitProvider; + myUnitProvider = (DateUnitProvider) aUnitProvider; // Sync the GUI to the state of the aEditable - protoNameList = myUnitProvider.getProtoNameList(); + var protoNameL = myUnitProvider.getProtoNameList(); - activeUnit = myUnitProvider.getUnit(); - activeTimeZone = activeUnit.getTimeZone(); + var activeUnit = myUnitProvider.getUnit(); + var activeTimeZone = activeUnit.getTimeZone(); // Synch the GUI with the UnitProvider custTF.setValue(myUnitProvider.getCustomPattern()); protoBox.removeAllItems(); - for (String aName : protoNameList) + for (String aName : protoNameL) protoBox.addItem(aName); protoBox.setChosenItem(myUnitProvider.getProtoUnit().getConfigName()); @@ -142,12 +155,6 @@ public class DateUnitPanel extends EditorPanel implements ActionListener */ private void buildGuiArea() { - CharDocument charDoc; - QueryComposer aComposer; - ItemHandler itemHandler; - StaticItemProcessor itemProcessor; - int targH; - setLayout(new MigLayout("", "0[left][grow]0", "0[][][][][][]0[grow]0")); // Example area @@ -158,19 +165,22 @@ public class DateUnitPanel extends EditorPanel implements ActionListener add(GuiUtil.createDivider(), "growx,h 4!,span,wrap"); // Specification area - typeRB = GuiUtil.createJRadioButton("Named:", this); + typeRB = GuiUtil.createJRadioButton(this, "Named:"); protoBox = new GComboBox(this); add("span 1", typeRB); add("growx,span,wrap", protoBox); // Custom area - custRB = GuiUtil.createJRadioButton("Custom:", this); + custRB = GuiUtil.createJRadioButton(this, "Custom:"); custTF = new GTextField(this); - charDoc = new CharDocument(custTF, "GyMwWDdEaHhmsSzZ :|\\/-,[](){}<>;.", true); + var charDoc = new CharDocument(custTF, "GyMwWDdEaHhmsSzZ :|\\/-,[](){}<>;.", true); custTF.setDocument(charDoc); add("span 1", custRB); add("growx,span,wrap", custTF); + // Link the radio buttons + GuiUtil.linkRadioButtons(custRB, typeRB); + // TimeZone area timeZoneL = new JLabel("TimeZone:"); timeZoneBox = new GComboBox(this, new TimeZoneCellRenderer()); @@ -180,29 +190,25 @@ public class DateUnitPanel extends EditorPanel implements ActionListener add("span 1", timeZoneL); add("growx,span,w 0:100:,wrap", timeZoneBox); - // Link the radio buttons - GuiUtil.linkRadioButtons(custRB, typeRB); - // Rules table - List itemList; - itemList = Lists.newLinkedList(); + var tmpItemL = new ArrayList(); for (String[] aRow : DescriptionArr) - itemList.add(new PlainRow(aRow[0], aRow[1], aRow[2])); + tmpItemL.add(new PlainRow(aRow[0], aRow[1], aRow[2])); - aComposer = new QueryComposer(); - aComposer.addAttribute(Lookup.Key, String.class, "Key", "Key"); - aComposer.addAttribute(Lookup.Comp, String.class, "Date Comp.", "Time Zone: General"); - aComposer.addAttribute(Lookup.Example, String.class, "Example", "Eastern; EST"); - aComposer.getItem(Lookup.Example).maxSize = 5000; + var tmpComposer = new QueryComposer(); + tmpComposer.addAttribute(Lookup.Key, String.class, "Key", "Key"); + tmpComposer.addAttribute(Lookup.Comp, String.class, "Date Comp.", "Time Zone: General"); + tmpComposer.addAttribute(Lookup.Example, String.class, "Example", "Eastern; EST"); + tmpComposer.getItem(Lookup.Example).maxSize = 5000; plainRenderer = new DefaultTableCellRenderer(); for (Lookup aEnum : Lookup.values()) - aComposer.setRenderer(aEnum, plainRenderer); + tmpComposer.setRenderer(aEnum, plainRenderer); - itemHandler = new QueryItemHandler(aComposer); - itemProcessor = new StaticItemProcessor(itemList); + var tmpIH = new QueryItemHandler(); + var tmpIP = new StaticItemProcessor<>(tmpItemL); - targH = ((custTF.getPreferredSize().height - 2) * DescriptionArr.length) + 2; - ruleLP = new ItemListPanel(itemHandler, itemProcessor, false, false); + var targH = ((custTF.getPreferredSize().height - 2) * DescriptionArr.length) + 2; + ruleLP = new ItemListPanel<>(tmpIH, tmpIP, tmpComposer, false); ruleLP.setPreferredSize(new Dimension(ruleLP.getPreferredSize().width, targH)); ruleLP.setSortingEnabled(false); ruleLP.setEnabled(false); @@ -214,13 +220,11 @@ public class DateUnitPanel extends EditorPanel implements ActionListener */ private void updateModel() { - TimeZone timeZone; - // Need a valid UnitProvider if (myUnitProvider == null) return; - timeZone = timeZoneBox.getChosenItem(); + var timeZone = timeZoneBox.getChosenItem(); if (typeRB.isSelected() == true) myUnitProvider.activateProto(timeZone, protoBox.getChosenItem()); else @@ -232,24 +236,19 @@ public class DateUnitPanel extends EditorPanel implements ActionListener */ private void updateGui() { - DateUnit activeUnit; - String exampleStr; - boolean isEnabled; - long currTime; - - isEnabled = custRB.isSelected(); + var isEnabled = custRB.isSelected(); custTF.setEnabled(isEnabled); protoBox.setEnabled(!isEnabled); // itemLP.repaint(); // Retrieve the activeUnit - activeUnit = null; + var activeUnit = (DateUnit) null; if (myUnitProvider != null) activeUnit = myUnitProvider.getUnit(); // Update the example area - currTime = System.currentTimeMillis(); - exampleStr = ""; + var currTime = System.currentTimeMillis(); + var exampleStr = ""; if (activeUnit != null) exampleStr = activeUnit.getString(currTime); @@ -258,15 +257,22 @@ public class DateUnitPanel extends EditorPanel implements ActionListener /** * Helper classes to aide with setting up the info table + * + * @author lopeznr1 */ enum Lookup { Key, Comp, Example }; + /** + * Helper classes to aide with setting up the info table + * + * @author lopeznr1 + */ class PlainRow implements QueryItem { - private String key, comp, example; + private final String key, comp, example; public PlainRow(String aKey, String aComp, String aExample) { @@ -281,16 +287,16 @@ public class DateUnitPanel extends EditorPanel implements ActionListener switch (aEnum) { case Key: - return key; + return key; case Comp: - return comp; + return comp; case Example: - return example; + return example; default: - return null; + return null; } } diff --git a/src/glum/gui/unit/DecimalUnitPanel.java b/src/glum/gui/unit/DecimalUnitPanel.java index af93d62..cb82aa3 100644 --- a/src/glum/gui/unit/DecimalUnitPanel.java +++ b/src/glum/gui/unit/DecimalUnitPanel.java @@ -1,22 +1,39 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.text.DecimalFormat; import java.util.List; -import javax.swing.*; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; + +import com.google.common.collect.Range; import glum.gui.GuiUtil; import glum.gui.component.GComboBox; import glum.gui.component.GNumberField; -import glum.unit.ConstUnitProvider; -import glum.unit.DecimalUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.Unit; -import glum.unit.UnitProvider; - +import glum.unit.*; import net.miginfocom.swing.MigLayout; +/** + * User input component that allows the user to specify a {@link DecimalUnitProvider}. + * + * @author lopeznr1 + */ public class DecimalUnitPanel extends EditorPanel implements ActionListener { // Gui components @@ -28,10 +45,9 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener // State vars private DecimalUnitProvider myUnitProvider; + /** Standard Constructor */ public DecimalUnitPanel() { - super(); - myUnitProvider = null; buildGuiArea(); @@ -50,26 +66,21 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener @Override public void setUnitProvider(UnitProvider aUnitProvider) { - int decimalPlaces; - boolean forceFullLabel; - List unitList; - Unit protoUnit, chosenUnit; - // Update our UnitProvider myUnitProvider = null; if (aUnitProvider instanceof DecimalUnitProvider) - myUnitProvider = (DecimalUnitProvider)aUnitProvider; + myUnitProvider = (DecimalUnitProvider) aUnitProvider; // Sync the GUI to the state of the aEditable - decimalPlaces = 0; - forceFullLabel = false; - unitList = null; - protoUnit = null; + var decimalPlaces = 0; + var forceFullLabel = false; + List unitL = null; + Unit protoUnit = null; if (myUnitProvider != null) { decimalPlaces = myUnitProvider.getDecimalPlaces(); forceFullLabel = myUnitProvider.getForceFullLabel(); - unitList = myUnitProvider.getProtoUnitList(); + unitL = myUnitProvider.getProtoUnitList(); protoUnit = myUnitProvider.getProtoUnit(); } @@ -77,9 +88,9 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener decimalPlacesNF.setValue(decimalPlaces); forceLongUnitsCB.setSelected(forceFullLabel); - chosenUnit = null; + Unit chosenUnit = null; unitBox.removeAllItems(); - for (Unit aUnit : unitList) + for (Unit aUnit : unitL) { unitBox.addItem(aUnit); if (aUnit == protoUnit) @@ -97,10 +108,8 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener */ private void buildGuiArea() { - UnitProvider countUP; - setLayout(new MigLayout("", "0[right][][grow]0", "0[][]0[]0")); - countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, new DecimalFormat("###,###,###,###,##0"))); + var countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, new DecimalFormat("###,###,###,###,##0"))); // Unit area unitL = new JLabel("Unit:"); @@ -109,8 +118,9 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener add("growx,span,wrap", unitBox); // DecimalPlaces area + var tmpRange = Range.closed(0.0, 9.0); decimalPlacesL = new JLabel("Decimal Places:"); - decimalPlacesNF = new GNumberField(this, countUP, 0, 9); + decimalPlacesNF = new GNumberField(this, countUP, tmpRange); add("span 2", decimalPlacesL); add("growx,span 1,wrap", decimalPlacesNF); @@ -124,16 +134,13 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener */ private void updateGui() { - Unit protoUnit; - boolean isEnabled; - // Need a valid UnitProvider if (myUnitProvider == null) return; // Synch the gui components - protoUnit = unitBox.getChosenItem(); - isEnabled = (protoUnit instanceof NumberUnit); + var protoUnit = unitBox.getChosenItem(); + var isEnabled = (protoUnit instanceof NumberUnit); forceLongUnitsCB.setEnabled(isEnabled); } @@ -142,14 +149,10 @@ public class DecimalUnitPanel extends EditorPanel implements ActionListener */ private void updateModel() { - Unit protoUnit; - int decimalPlaces; - boolean forceLongUnits; - // Get the gui configuration - protoUnit = unitBox.getChosenItem(); - decimalPlaces = decimalPlacesNF.getValueAsInt(0); - forceLongUnits = forceLongUnitsCB.isSelected(); + var protoUnit = unitBox.getChosenItem(); + var decimalPlaces = decimalPlacesNF.getValueAsInt(0); + var forceLongUnits = forceLongUnitsCB.isSelected(); // Update the UnitProvider myUnitProvider.activate(protoUnit, decimalPlaces, forceLongUnits); diff --git a/src/glum/gui/unit/EditorPanel.java b/src/glum/gui/unit/EditorPanel.java index 75abeea..7f9701c 100644 --- a/src/glum/gui/unit/EditorPanel.java +++ b/src/glum/gui/unit/EditorPanel.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import glum.gui.panel.GPanel; diff --git a/src/glum/gui/unit/LatLonUnitPanel.java b/src/glum/gui/unit/LatLonUnitPanel.java deleted file mode 100644 index 4f933d1..0000000 --- a/src/glum/gui/unit/LatLonUnitPanel.java +++ /dev/null @@ -1,196 +0,0 @@ -package glum.gui.unit; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.text.DecimalFormat; -import javax.swing.*; - -import net.miginfocom.swing.MigLayout; - -import glum.gui.GuiUtil; -import glum.gui.component.GComboBox; -import glum.gui.component.GNumberField; -import glum.gui.panel.CardPanel; -import glum.unit.ConstUnitProvider; -import glum.unit.LatLonUnitProvider; -import glum.unit.NumberUnit; -import glum.unit.UnitProvider; - -public class LatLonUnitPanel extends EditorPanel implements ActionListener -{ - // Gui components - private GComboBox unitBox; - private CardPanel editPanel; - private GNumberField decimalPlacesNF; - private JCheckBox isZeroCenteredCB; - private JCheckBox isSecondsShownCB; - - // State vars - private LatLonUnitProvider myUnitProvider; - - public LatLonUnitPanel() - { - super(); - - myUnitProvider = null; - buildGuiArea(); - updateGui(); - } - - @Override - public void actionPerformed(ActionEvent aEvent) - { - Object source; - - source = aEvent.getSource(); - if (source == unitBox) - updateGui(); - - updateEditable(); - notifyListeners(this, ID_UPDATE, "unit.update"); - } - - @Override - public void setUnitProvider(UnitProvider aUnitProvider) - { - String unitTypeStr; - int decimalPlaces; - boolean isSecondsShown; - boolean isZeroCentered; - - // Update our UnitProvider - myUnitProvider = null; - if (aUnitProvider instanceof LatLonUnitProvider) - myUnitProvider = (LatLonUnitProvider)aUnitProvider; - - // Sync the GUI to the state of the UnitProvider - decimalPlaces = decimalPlacesNF.getValueAsInt(0); - isSecondsShown = isSecondsShownCB.isSelected(); - isZeroCentered = isZeroCenteredCB.isSelected(); - unitTypeStr = "Raw"; - if (myUnitProvider != null) - { - if (myUnitProvider.isRawUnitActive() == true) - { - decimalPlaces = myUnitProvider.getDecimalPlaces(); - isZeroCentered = myUnitProvider.isZeroCentered(); - unitTypeStr = "Raw"; - } - else - { - isSecondsShown = myUnitProvider.isSecondsShown(); - unitTypeStr = "Standard"; - } - } - - // Synch the GUI with the UnitProvider - decimalPlacesNF.setValue(decimalPlaces); - isSecondsShownCB.setSelected(isSecondsShown); - isZeroCenteredCB.setSelected(isZeroCentered); - - unitBox.removeActionListener(this); - unitBox.setSelectedItem(unitTypeStr); - unitBox.addActionListener(this); - - updateGui(); - } - - /** - * Forms the GUI - */ - private void buildGuiArea() - { - JLabel unitL; - - setLayout(new MigLayout("", "0[right][][grow]0", "0[][grow]0")); - - // Unit area - unitL = new JLabel("Unit:", JLabel.CENTER); - unitBox = new GComboBox(this, "Raw", "Standard"); - add("span 1", unitL); - add("growx,span,wrap", unitBox); - - // Edit area - editPanel = new CardPanel(); - editPanel.addCard("Raw", formRawPanel()); - editPanel.addCard("Standard", formStandardPanel()); - add("growx,growy,span", editPanel); - } - - /** - * Forms the panel to configure the raw unit for lat/lon - */ - private JPanel formRawPanel() - { - JPanel tmpPanel; - JLabel tmpL; - UnitProvider countUP; - - tmpPanel = new JPanel(new MigLayout("", "0[right][][grow]0", "0[]0")); - countUP = new ConstUnitProvider(new NumberUnit("", "", 1.0, new DecimalFormat("###,###,###,###,##0"))); - - // DecimalPlaces area - tmpL = new JLabel("Decimal Places:", JLabel.CENTER); - decimalPlacesNF = new GNumberField(this, countUP, 0, 9); - tmpPanel.add("span 2", tmpL); - tmpPanel.add("growx,span 1,wrap", decimalPlacesNF); - - // Zero centered area - isZeroCenteredCB = GuiUtil.createJCheckBox("Zero Centered", this); - tmpPanel.add("align left,span", isZeroCenteredCB); - - return tmpPanel; - } - - /** - * Forms the panel to configure the standard unit for lat/lon - */ - private JPanel formStandardPanel() - { - JPanel tmpPanel; - - tmpPanel = new JPanel(new MigLayout("", "0[grow]0", "0[]0")); - - isSecondsShownCB = GuiUtil.createJCheckBox("Show Seconds", this); - tmpPanel.add("span 1", isSecondsShownCB); - - return tmpPanel; - } - - /** - * Updates the GUI - */ - private void updateGui() - { - String unitTypeStr; - - unitTypeStr = unitBox.getChosenItem(); - editPanel.switchToCard(unitTypeStr); - } - - /** - * Updates the UnitProvider - */ - private void updateEditable() - { - String unitTypeStr; - boolean isSecondsShown; - boolean isZeroCentered; - int decimalPlaces; - - // Bail if no UnitProvider - if (myUnitProvider == null) - return; - - isSecondsShown = isSecondsShownCB.isSelected(); - isZeroCentered = isZeroCenteredCB.isSelected(); - decimalPlaces = decimalPlacesNF.getValueAsInt(0); - - unitTypeStr = unitBox.getChosenItem(); - if (unitTypeStr.equals("Raw") == true) - myUnitProvider.activateRaw(decimalPlaces, isZeroCentered); - else - myUnitProvider.activateStandard(isSecondsShown); - } - -} diff --git a/src/glum/gui/unit/TimeZoneCellRenderer.java b/src/glum/gui/unit/TimeZoneCellRenderer.java index 6a3dc75..339945f 100644 --- a/src/glum/gui/unit/TimeZoneCellRenderer.java +++ b/src/glum/gui/unit/TimeZoneCellRenderer.java @@ -1,30 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import java.awt.Component; import java.util.TimeZone; -import javax.swing.DefaultListCellRenderer; -import javax.swing.JLabel; -import javax.swing.JList; +import javax.swing.*; +/** + * ListCellRenderer that displays the ID of the specified {@link TimeZone}. + * + * @author lopeznr1 + */ public class TimeZoneCellRenderer extends DefaultListCellRenderer { + /** Standard Constructor */ public TimeZoneCellRenderer() { - super(); + ; // Nothing to do } @Override - public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, boolean hasFocus) + public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, + boolean hasFocus) { - JLabel retL; - String aStr; - - retL = (JLabel)super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); + var retL = (JLabel) super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); if (aObj instanceof TimeZone) { - aStr = ((TimeZone)aObj).getID(); - retL.setText(aStr); + var tmpStr = ((TimeZone) aObj).getID(); + retL.setText(tmpStr); } return retL; diff --git a/src/glum/gui/unit/UnitConfigurationDialog.java b/src/glum/gui/unit/UnitConfigurationDialog.java index 178f317..f83eb40 100644 --- a/src/glum/gui/unit/UnitConfigurationDialog.java +++ b/src/glum/gui/unit/UnitConfigurationDialog.java @@ -1,14 +1,26 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.List; +import java.util.ArrayList; import javax.swing.*; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; -import javax.swing.table.*; import glum.gui.FocusUtil; import glum.gui.GuiUtil; @@ -20,10 +32,15 @@ import glum.unit.UnitListener; import glum.unit.UnitProvider; import net.miginfocom.swing.MigLayout; +/** + * JDialog that allows for the configuration of UnitProviders. + * + * @author lopeznr1 + */ public class UnitConfigurationDialog extends JDialog implements ActionListener, ListSelectionListener, UnitListener { // Gui Components - private ItemListPanel itemLP; + private ItemListPanel itemLP; private CardPanel editorPanel; private JLabel titleL; private JButton closeB; @@ -31,6 +48,7 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, // State vars private StaticItemProcessor itemProcessor; + /** Standard Constructor */ public UnitConfigurationDialog(JFrame aParentFrame) { // Make sure we call the parent @@ -59,8 +77,6 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, */ public void addEditorPanel(Class aClass, EditorPanel aPanel) { - Dimension aDim; - // Insanity check if (aClass == null || aPanel == null) return; @@ -70,15 +86,15 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, editorPanel.switchToCard("null"); updateGui(); - aDim = editorPanel.getMinimumSize(); + var tmpDim = editorPanel.getMinimumSize(); for (EditorPanel evalPanel : editorPanel.getAllCards()) { - if (evalPanel.getMinimumSize().height > aDim.height) - aDim.height = evalPanel.getMinimumSize().height; + if (evalPanel.getMinimumSize().height > tmpDim.height) + tmpDim.height = evalPanel.getMinimumSize().height; } // Set the dialog to the best initial size - setSize(getPreferredSize().width, itemLP.getHeight() + closeB.getHeight() + titleL.getHeight() + aDim.height); + setSize(getPreferredSize().width, itemLP.getHeight() + closeB.getHeight() + titleL.getHeight() + tmpDim.height); } /** @@ -86,8 +102,6 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, */ public void addUnitProvider(UnitProvider aUnitProvider) { - List itemList; - // Insanity check if (aUnitProvider == null) return; @@ -96,10 +110,9 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, aUnitProvider.addListener(this); // Update the table processor - itemList = itemProcessor.getItems(); - itemList.add(aUnitProvider); - ; - itemProcessor.setItems(itemList); + var itemL = new ArrayList<>(itemProcessor.getAllItems()); + itemL.add(aUnitProvider); + itemProcessor.setItems(itemL); // Update the dialog updateTable(); @@ -108,9 +121,7 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, @Override public void actionPerformed(ActionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == closeB) { setVisible(false); @@ -126,9 +137,7 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, @Override public void valueChanged(ListSelectionEvent aEvent) { - Object source; - - source = aEvent.getSource(); + var source = aEvent.getSource(); if (source == itemLP) { if (aEvent.getValueIsAdjusting() == true) @@ -143,10 +152,6 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, */ private void buildGuiArea() { - QueryComposer aComposer; - ItemHandler itemHandler; - EditorPanel nullPanel; - setLayout(new MigLayout("", "[center,grow]", "2[]3[grow][fill,growprio 200][]")); // Build the title label @@ -154,20 +159,20 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, add("span,wrap", titleL); // UnitProvider table - aComposer = new QueryComposer(); - aComposer.addAttribute(Lookup.Key, String.class, "Type", null); - aComposer.addAttribute(Lookup.Value, String.class, "Value", null); + var tmpComposer = new QueryComposer(); + tmpComposer.addAttribute(LookUp.Key, String.class, "Type", null); + tmpComposer.addAttribute(LookUp.Value, String.class, "Value", null); - itemHandler = new UnitProviderHandler(aComposer); - itemProcessor = new StaticItemProcessor(); + var tmpIH = new UnitProviderHandler(); + itemProcessor = new StaticItemProcessor<>(); - itemLP = new ItemListPanel(itemHandler, itemProcessor, false, false); + itemLP = new ItemListPanel<>(tmpIH, itemProcessor, tmpComposer, false); itemLP.addListSelectionListener(this); itemLP.setSortingEnabled(false); add("growx,growy,h 70::,span,wrap", itemLP); // Form the editor area - nullPanel = new EditorPanel(); + var nullPanel = new EditorPanel(); nullPanel.setPreferredSize(new Dimension(1, 1)); editorPanel = new CardPanel(); @@ -184,35 +189,29 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, */ private void updateGui() { - EditorPanel aPanel; - UnitProvider aUnitProvider; - Dimension aDim; - String cardName; - // Get the selected UnitProvider - aUnitProvider = itemLP.getSelectedItem(); + var tmpUnitProvider = itemLP.getSelectedItem(); // Switch to the appropriate Editor - cardName = "null"; - if (aUnitProvider != null) - cardName = "" + aUnitProvider.getClass(); + var cardName = "null"; + if (tmpUnitProvider != null) + cardName = "" + tmpUnitProvider.getClass(); editorPanel.switchToCard(cardName); // Resize the editorPanel to be as compact as the active card - aPanel = editorPanel.getActiveCard(); - aPanel.setUnitProvider(aUnitProvider); - aDim = aPanel.getPreferredSize(); + var tmpPanel = editorPanel.getActiveCard(); + tmpPanel.setUnitProvider(tmpUnitProvider); + var tmpDim = tmpPanel.getPreferredSize(); // System.out.println("minHeight: " + aDim.getHeight() + " hmm: " + aPanel); - editorPanel.setMaximumSize(new Dimension(5000, aDim.height)); - // Hack to get the editorPanel resize properly. Not sure why invalidate(), validate() do not work + editorPanel.setMaximumSize(new Dimension(5000, tmpDim.height)); + // Hack to get the editorPanel resize properly. Not sure why invalidate(), + // validate() do not work int aHeight = getHeight(); - Runnable tmpRunnable1 = () -> setSize(getWidth(), aHeight - 1); - Runnable tmpRunnable2 = () -> setSize(getWidth(), aHeight); - SwingUtilities.invokeLater(tmpRunnable1); - SwingUtilities.invokeLater(tmpRunnable2); -// Runnable tmpRunnable1 = () -> editorPanel.invalidate(); -// Runnable tmpRunnable2 = () -> editorPanel.validate(); + SwingUtilities.invokeLater(() -> setSize(getWidth(), aHeight - 1)); + SwingUtilities.invokeLater(() -> setSize(getWidth(), aHeight)); +// SwingUtilities.invokeLater(() -> editorPanel.invalidate()); +// SwingUtilities.invokeLater(() -> editorPanel.validate()); // invalidate(); // validate(); } @@ -222,80 +221,81 @@ public class UnitConfigurationDialog extends JDialog implements ActionListener, */ private void updateTable() { - List itemList; - JTableHeader aTableHeader; - TableColumnModel aTableColumnModel; - TableColumn aTableColumn; - int aWidth, tmpWidth; - JLabel aLabel; - // Update myTable column[0] width - aWidth = 10; - aLabel = new JLabel(""); - itemList = itemProcessor.getItems(); - for (UnitProvider aUnitProvider : itemList) + var aWidth = 10; + var tmpLabel = new JLabel(""); + var itemL = itemProcessor.getAllItems(); + for (UnitProvider aUnitProvider : itemL) { - aLabel.setText(aUnitProvider.getDisplayName()); - tmpWidth = aLabel.getPreferredSize().width + 5; + tmpLabel.setText(aUnitProvider.getDisplayName()); + var tmpWidth = tmpLabel.getPreferredSize().width + 5; if (aWidth < tmpWidth) aWidth = tmpWidth; } // Set sizing attributes of the column 1 - aTableHeader = itemLP.getTable().getTableHeader(); - aTableColumnModel = aTableHeader.getColumnModel(); - aTableColumn = aTableColumnModel.getColumn(0); - aTableColumn.setResizable(false); - aTableColumn.setMinWidth(aWidth); - aTableColumn.setMaxWidth(aWidth); - aTableColumn.setPreferredWidth(aWidth); + var tmpTableHeader = itemLP.getTable().getTableHeader(); + var tmpTableColumnModel = tmpTableHeader.getColumnModel(); + var tmpTableColumn = tmpTableColumnModel.getColumn(0); + tmpTableColumn.setResizable(false); + tmpTableColumn.setMinWidth(aWidth); + tmpTableColumn.setMaxWidth(aWidth); + tmpTableColumn.setPreferredWidth(aWidth); } /** - * Helper classes to aide with setting up the UnitProvider table + * Enum which defines the types used to show a listing of configurable units. + * + * @author lopeznr1 */ - enum Lookup + enum LookUp { Key, + Value, }; - public class UnitProviderHandler extends BasicItemHandler + /** + * Implementation of {@link ItemHandler} that allows for tabular access of {@link UnitProvider}s. + * + * @author lopeznr1 + */ + public class UnitProviderHandler implements ItemHandler { - public UnitProviderHandler(QueryComposer aComposer) - { - super(aComposer); - } - @Override - public Object getColumnValue(UnitProvider aItem, int aColNum) + public Object getValue(UnitProvider aItem, LookUp aEnum) { - Enum refKey; - - // Insanity check - if (aColNum < 0 && aColNum >= fullAttributeList.size()) - return null; - - refKey = fullAttributeList.get(aColNum).refKey; - switch ((Lookup)refKey) + switch (aEnum) { case Key: return aItem.getDisplayName(); case Value: - return aItem.getConfigName(); + return getConfigName(aItem); default: break; } - return null; + throw new RuntimeException("Unsupported enum:" + aEnum); } @Override - public void setColumnValue(UnitProvider aItem, int colNum, Object aValue) + public void setValue(UnitProvider aItem, LookUp aEnum, Object aValue) { - throw new RuntimeException("Unsupported Operation."); + throw new RuntimeException("Unsupported enum:" + aEnum); + } + + /** + * Helper method that returns the configuration name to utilize. + */ + public String getConfigName(UnitProvider aUnitProvider) + { + var tmpUnit = aUnitProvider.getUnit(); + if (tmpUnit == null) + return "None"; + + return tmpUnit.getConfigName(); } } diff --git a/src/glum/gui/unit/UnitLabelRenderer.java b/src/glum/gui/unit/UnitLabelRenderer.java index b566e3f..186a960 100644 --- a/src/glum/gui/unit/UnitLabelRenderer.java +++ b/src/glum/gui/unit/UnitLabelRenderer.java @@ -1,28 +1,46 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.gui.unit; import java.awt.Component; + import javax.swing.*; import glum.unit.Unit; +/** + * ListCellRenderer that displays the {@link Unit}'s configuration name. + * + * @author lopeznr1 + */ public class UnitLabelRenderer extends DefaultListCellRenderer { + /** Standard Constructor */ public UnitLabelRenderer() { - super(); + ; // Nothing to do } @Override - public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, boolean hasFocus) + public Component getListCellRendererComponent(JList list, Object aObj, int index, boolean isSelected, + boolean hasFocus) { - JLabel retL; - String aStr; - - retL = (JLabel)super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); + var retL = (JLabel) super.getListCellRendererComponent(list, aObj, index, isSelected, hasFocus); if (aObj instanceof Unit) { - aStr = ((Unit)aObj).getConfigName(); - retL.setText(aStr); + var tmpStr = ((Unit) aObj).getConfigName(); + retL.setText(tmpStr); } return retL; diff --git a/src/glum/io/ConfigMapTP.java b/src/glum/io/ConfigMapTP.java index 1091c9b..77d1431 100644 --- a/src/glum/io/ConfigMapTP.java +++ b/src/glum/io/ConfigMapTP.java @@ -1,12 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; import glum.registry.ConfigMap; +/** + * Implementation of {@link TokenProcessor} used to populate a {@link ConfigMap}. + * + * @author lopeznr1 + */ public class ConfigMapTP implements TokenProcessor { // State vars - ConfigMap refConfigMap; + private ConfigMap refConfigMap; + /** Standard Constructor */ public ConfigMapTP(ConfigMap aConfigMap) { refConfigMap = aConfigMap; @@ -19,20 +38,20 @@ public class ConfigMapTP implements TokenProcessor } @Override - public boolean process(String[] tokens, int lineNum) + public boolean process(String[] aTokenArr, int aLineNum) { // Insanity check - if (tokens == null || tokens.length <= 1) + if (aTokenArr == null || aTokenArr.length <= 1) return false; - if (tokens.length == 2) + if (aTokenArr.length == 2) { - refConfigMap.put(tokens[0], tokens[1]); + refConfigMap.put(aTokenArr[0], aTokenArr[1]); } else { - for (int c1 = 1; c1 < tokens.length; c1++) - refConfigMap.addItem(tokens[0], tokens[c1]); + for (int c1 = 1; c1 < aTokenArr.length; c1++) + refConfigMap.addItem(aTokenArr[0], aTokenArr[c1]); } return true; diff --git a/src/glum/io/IoUtil.java b/src/glum/io/IoUtil.java index 169fe8e..8f53946 100644 --- a/src/glum/io/IoUtil.java +++ b/src/glum/io/IoUtil.java @@ -1,17 +1,33 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; -import glum.task.*; -import glum.zio.ZinStream; -import glum.zio.ZoutStream; - import java.io.*; import java.net.*; import java.nio.channels.Channel; -import java.util.Base64; -import java.util.Map; +import java.util.*; -import com.google.common.collect.Maps; +import glum.task.ConsoleTask; +import glum.task.Task; +import glum.zio.ZinStream; +import glum.zio.ZoutStream; +/** + * Collection of various utility methods. + * + * @author lopeznr1 + */ public class IoUtil { /** @@ -19,27 +35,14 @@ public class IoUtil */ public static URL createURL(String aUrlStr) { - URL retURL; - try { - retURL = new URL(aUrlStr); + return new URL(aUrlStr); } - catch(MalformedURLException e) + catch (MalformedURLException e) { - retURL = null; + return null; } - - return retURL; - } - - /** - * Prints out the error msg followed by the stack trace. - */ - public static void dumpTrace(Exception aExp, String aMsg) - { - System.out.println(aMsg); - aExp.printStackTrace(); } /** @@ -54,7 +57,7 @@ public class IoUtil { aChannel.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -72,7 +75,7 @@ public class IoUtil { aStream.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -90,7 +93,7 @@ public class IoUtil { aStream.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -108,7 +111,7 @@ public class IoUtil { aReader.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -126,7 +129,7 @@ public class IoUtil { aWriter.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -145,7 +148,7 @@ public class IoUtil { aStream.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -163,17 +166,55 @@ public class IoUtil { aStream.close(); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } } + /** + * Utility helper that will locate the next available {@link File} with a name closest to that of aFile. + *

    + * The next available {@link File} is defined as a file that does not exist. + *

    + * Names will be searched by appending an increasing index (starting from zero) onto the file name until a + * {@link File} is located that does not exist (on the local file system). + */ + public static File locateNextAvailableFile(File aFile) + { + // If the File does not exist then just utilize it as is + if (aFile.exists() == false) + return aFile; + + // Extract the path's components of interest + File basePath = aFile.getParentFile(); + String baseName = aFile.getName(); + String fileExt = ""; + + String tmpFileName = aFile.getName(); + int tmpIdx = tmpFileName.lastIndexOf("."); + if (tmpIdx != -1 && tmpIdx != tmpFileName.length() - 1) + { + baseName = tmpFileName.substring(0, tmpIdx); + fileExt = tmpFileName.substring(tmpIdx); + } + + int currIdx = 0; + while (true) + { + File tmpFile = new File(basePath, baseName + "." + currIdx + fileExt); + if (tmpFile.exists() == false) + return tmpFile; + + currIdx++; + } + } + /** * Downloads the content at aUrl and saves it to aFile. Before the file is downloaded, the connection is configured * with the specified property map. The download will be aborted if aTask is no longer active. */ - public static boolean copyUrlToFile(Task aTask, URL aUrl, File aFile, Map aPropertyMap) + public static boolean copyUrlToFile(Task aTask, URL aUrl, File aFile, Map aPropertyM) { // Ensure we have a valid aTask if (aTask == null) @@ -193,10 +234,10 @@ public class IoUtil aConnection.setReadTimeout(90 * 1000); // Setup the various properties - if (aPropertyMap != null) + if (aPropertyM != null) { - for (String aKey : aPropertyMap.keySet()) - aConnection.setRequestProperty(aKey, aPropertyMap.get(aKey)); + for (String aKey : aPropertyM.keySet()) + aConnection.setRequestProperty(aKey, aPropertyM.get(aKey)); } inStream = aConnection.getInputStream(); @@ -216,16 +257,16 @@ public class IoUtil // Bail if aTask is aborted if (aTask.isActive() == false) { - aTask.infoAppendln("Download of file: " + aFile + " has been aborted!"); + aTask.logRegln("Download of file: " + aFile + " has been aborted!"); return false; } } } - catch(Exception aExp) + catch (Exception aExp) { - aTask.infoAppendln("Exception:" + aExp); - aTask.infoAppendln(" URL:" + aUrl); - aTask.infoAppendln(" File:" + aFile); + aTask.logRegln("Exception:" + aExp); + aTask.logRegln(" URL:" + aUrl); + aTask.logRegln(" File:" + aFile); aExp.printStackTrace(); return false; } @@ -261,13 +302,13 @@ public class IoUtil */ public static boolean copyUrlToFile(Task aTask, URL aUrl, File aFile, String aUsername, String aPassword) { - String authStr = aUsername + ":" + aPassword; + var authStr = aUsername + ":" + aPassword; authStr = Base64.getEncoder().encodeToString(authStr.getBytes()); - Map plainMap = Maps.newHashMap(); - plainMap.put("Authorization", "Basic " + authStr); + var plainM = new HashMap(); + plainM.put("Authorization", "Basic " + authStr); - return copyUrlToFile(aTask, aUrl, aFile, plainMap); + return copyUrlToFile(aTask, aUrl, aFile, plainM); } /** @@ -289,9 +330,8 @@ public class IoUtil { tmpUrl = aFile1.toURI().toURL(); } - catch(Exception aExp) + catch (Exception aExp) { - System.out.println("Exception:" + aExp); aExp.printStackTrace(); return false; } @@ -301,9 +341,8 @@ public class IoUtil /** * Method to recursively delete all of the contents located in the specified directory. - *

    + *

    * Source: http://stackoverflow.com/questions/3775694/deleting-folder-from-java - *

    */ public static boolean deleteDirectory(File directory) { @@ -368,7 +407,8 @@ public class IoUtil // Ensure the string size is less than 0x00FFFF if (size >= 0x00FFFF) - throw new RuntimeException("Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); + throw new IOException( + "Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); // Write out the string aStream.writeShort(size & 0x00FFFF); @@ -390,15 +430,16 @@ public class IoUtil data = aStr.getBytes("UTF-8"); size = data.length; } - catch(Exception aExp) + catch (Exception aExp) { throw new RuntimeException("UTF-8 Transform error.", aExp); } if (size >= 0x00FFFF) - throw new RuntimeException("Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); + throw new RuntimeException( + "Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); - return (short)(2 + data.length); + return (short) (2 + data.length); } } diff --git a/src/glum/io/Loader.java b/src/glum/io/Loader.java index 0fa41c3..a90afb0 100644 --- a/src/glum/io/Loader.java +++ b/src/glum/io/Loader.java @@ -1,19 +1,37 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; -import java.awt.*; +import java.awt.Component; import java.io.*; -import java.net.*; +import java.net.URL; import java.util.*; -import java.util.List; -import javax.swing.*; + +import javax.swing.JFileChooser; import javax.swing.filechooser.FileFilter; -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import glum.io.token.MatchTokenizer; import glum.io.token.Tokenizer; import glum.task.Task; +/** + * Collection of utility method for loading a text file using {@link TokenProcessor}s and and a {@link Tokenizer}. + * + * @author lopeznr1 + */ public class Loader { // Constants @@ -30,11 +48,8 @@ public class Loader public static void loadAsciiFile(File aFile, TokenProcessor aTokenProcessor, Tokenizer aTokenizer) { - Collection tpSet; - - tpSet = new LinkedList(); - tpSet.add(aTokenProcessor); - loadAsciiFile(aFile, tpSet, aTokenizer); + var tmpTokenProcessorL = ImmutableList.of(aTokenProcessor); + loadAsciiFile(aFile, tmpTokenProcessorL, aTokenizer); } public static void loadAsciiFile(File aFile, Collection tpSet) @@ -49,16 +64,14 @@ public class Loader public static void loadAsciiFile(File aFile, Collection tpSet, Tokenizer aTokenizer, Task aTask) { - URL aUrl; - try { - aUrl = aFile.toURI().toURL(); - loadAsciiFile(aUrl, tpSet, aTokenizer, aTask); + var tmpUrl = aFile.toURI().toURL(); + loadAsciiFile(tmpUrl, tpSet, aTokenizer, aTask); } - catch (Exception e) + catch (Exception aExp) { - e.printStackTrace(); + aExp.printStackTrace(); System.out.println("Resource not processed: " + aFile); return; } @@ -66,11 +79,8 @@ public class Loader public static void loadAsciiFile(URL aUrl, TokenProcessor aTokenProcessor) { - Collection tpSet; - - tpSet = new LinkedList(); - tpSet.add(aTokenProcessor); - loadAsciiFile(aUrl, tpSet, new MatchTokenizer(DEFAULT_REG_EX)); + var tmpTokenProcessorL = ImmutableList.of(aTokenProcessor); + loadAsciiFile(aUrl, tmpTokenProcessorL, new MatchTokenizer(DEFAULT_REG_EX)); } public static void loadAsciiFile(URL aUrl, Collection tpSet) @@ -80,38 +90,30 @@ public class Loader public static void loadAsciiFile(URL aUrl, Collection tpSet, Tokenizer aTokenizer) { - loadAsciiFile(aUrl, tpSet, aTokenizer, null); + loadAsciiFile(aUrl, tpSet, aTokenizer, null); } - + public static void loadAsciiFile(URL aUrl, Collection tpSet, Tokenizer aTokenizer, Task aTask) { - InputStream inStream; - // Insanity check if (aUrl == null) return; // Process our input - inStream = null; - try + try (var aStream = aUrl.openStream();) { - inStream = aUrl.openStream(); - loadAsciiFile(inStream, tpSet, aTokenizer, aTask); + loadAsciiFile(aStream, tpSet, aTokenizer, aTask); } - catch (FileNotFoundException e) + catch (FileNotFoundException aExp) { System.out.println("Resource not found: " + aUrl); return; } - catch (IOException e) + catch (IOException aExp) { System.out.println("Ioexception occured while loading: " + aUrl); return; } - finally - { - IoUtil.forceClose(inStream); - } } public static void loadAsciiFile(InputStream inStream, TokenProcessor aTokenProcessor) throws IOException @@ -119,18 +121,17 @@ public class Loader loadAsciiFile(inStream, aTokenProcessor, new MatchTokenizer(DEFAULT_REG_EX)); } - public static void loadAsciiFile(InputStream inStream, TokenProcessor aTokenProcessor, Tokenizer aTokenizer) throws IOException + public static void loadAsciiFile(InputStream inStream, TokenProcessor aTokenProcessor, Tokenizer aTokenizer) + throws IOException { loadAsciiFile(inStream, aTokenProcessor, aTokenizer, null); } - public static void loadAsciiFile(InputStream inStream, TokenProcessor aTokenProcessor, Tokenizer aTokenizer, Task aTask) throws IOException + public static void loadAsciiFile(InputStream inStream, TokenProcessor aTokenProcessor, Tokenizer aTokenizer, + Task aTask) throws IOException { - Collection tpSet; - - tpSet = new LinkedList(); - tpSet.add(aTokenProcessor); - loadAsciiFile(inStream, tpSet, aTokenizer, aTask); + var tmpTokenProcessorL = ImmutableList.of(aTokenProcessor); + loadAsciiFile(inStream, tmpTokenProcessorL, aTokenizer, aTask); } public static void loadAsciiFile(InputStream inStream, Collection tpSet) throws IOException @@ -138,23 +139,17 @@ public class Loader loadAsciiFile(inStream, tpSet, new MatchTokenizer(DEFAULT_REG_EX), null); } - public static void loadAsciiFile(InputStream inStream, Collection tpSet, Tokenizer aTokenizer, Task aTask) throws IOException + public static void loadAsciiFile(InputStream inStream, Collection tpSet, Tokenizer aTokenizer, + Task aTask) throws IOException { - BufferedReader br; - String strLine; - int lineNum; - ArrayList aList; - String tokens[], dummyVar[]; - boolean isProcessed; - // Insanity check if (tpSet == null) return; // Process our input - br = new BufferedReader( new InputStreamReader(inStream) ); - lineNum = 0; - dummyVar = new String[1]; + var tmpBR = new BufferedReader(new InputStreamReader(inStream)); + var lineNum = 0; + var dummyVar = new String[1]; // Read the lines while (true) @@ -162,8 +157,8 @@ public class Loader // Bail if the associated task is no longer active if (aTask != null && aTask.isActive() == false) return; - - strLine = br.readLine(); + + var strLine = tmpBR.readLine(); if (strLine == null) { // Notify the TokenProcessors of job done @@ -171,25 +166,24 @@ public class Loader aTP.flush(); // Release the various streams - br.close(); + tmpBR.close(); inStream.close(); break; } lineNum++; // Get the tokens out of our string - tokens = null; - aList = aTokenizer.getTokens(strLine); - if (aList.size() > 0) + var tokenL = aTokenizer.getTokens(strLine); + if (tokenL.size() > 0) { // Transform from a list to an array - tokens = aList.toArray(dummyVar); + var tokenArr = tokenL.toArray(dummyVar); // Process the tokens - isProcessed = false; + var isProcessed = false; for (TokenProcessor aTP : tpSet) { - isProcessed = aTP.process(tokens, lineNum); + isProcessed = aTP.process(tokenArr, lineNum); if (isProcessed == true) break; } @@ -205,188 +199,184 @@ public class Loader /** * Prompts the user to select a single File - * + * * @param aLoaderInfo - * : A object that stores the configuration from method call to method call. - * @param parentComp - * The parent component for the associated FileChooser GUI + * : A object that stores the configuration from method call to method call. + * @param aParentComp + * The parent component for the associated FileChooser GUI * @param aTitleStr - * The title of the FileChooser GUI - * @param ffList - * A List of FileFilters - * @param isSaveDialog - * Whether this FileChooser displays a GUI appropriate for saving a file + * The title of the FileChooser GUI + * @param aFileFilterC + * A List of FileFilters + * @param aIsSaveDialog + * Whether this FileChooser displays a GUI appropriate for saving a file * @return The selected file or null */ - public static File queryUserForFile(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, Collection ffList, boolean isSaveDialog) + public static File queryUserForFile(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + Collection aFileFilterC, boolean aIsSaveDialog) { - JFileChooser aFC; - int aVal; - // Ensure we have a non null LoaderInfo if (aLoaderInfo == null) aLoaderInfo = new LoaderInfo(); // Set up the FileChooser - aFC = new StandardFileChooser(null); - aFC.setAcceptAllFileFilterUsed(false); - aFC.setDialogTitle(aTitleStr); - aFC.setMultiSelectionEnabled(false); - aLoaderInfo.loadConfig(aFC); + var tmpFC = new StandardFileChooser(null); + tmpFC.setAcceptAllFileFilterUsed(false); + tmpFC.setDialogTitle(aTitleStr); + tmpFC.setMultiSelectionEnabled(false); + aLoaderInfo.loadConfig(tmpFC); // Set in the FileFilters - for (FileFilter aFileFilter : ffList) - aFC.addChoosableFileFilter(aFileFilter); + for (FileFilter aFileFilter : aFileFilterC) + tmpFC.addChoosableFileFilter(aFileFilter); // Let the user choose a file - if (isSaveDialog == true) - aVal = aFC.showSaveDialog(parentComp); + int tmpVal; + if (aIsSaveDialog == true) + tmpVal = tmpFC.showSaveDialog(aParentComp); else - aVal = aFC.showOpenDialog(parentComp); + tmpVal = tmpFC.showOpenDialog(aParentComp); // Store off the current settings - aLoaderInfo.saveConfig(aFC); + aLoaderInfo.saveConfig(tmpFC); // Bail if no file chosen - if (aVal != JFileChooser.APPROVE_OPTION) + if (tmpVal != JFileChooser.APPROVE_OPTION) return null; // Return the file - return aFC.getSelectedFile(); + return tmpFC.getSelectedFile(); } - public static File queryUserForFile(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, FileFilter aFileFilter, boolean isSaveDialog) + public static File queryUserForFile(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + FileFilter aFileFilter, boolean aIsSaveDialog) { - List ffList; - - ffList = Lists.newArrayList(); + var tmpFileFilterL = new ArrayList(); if (aFileFilter != null) - ffList.add(aFileFilter); - - return queryUserForFile(aLoaderInfo, parentComp, aTitleStr, ffList, isSaveDialog); + tmpFileFilterL.add(aFileFilter); + + return queryUserForFile(aLoaderInfo, aParentComp, aTitleStr, tmpFileFilterL, aIsSaveDialog); } - - public static File queryUserForFile(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, boolean isSaveDialog) + + public static File queryUserForFile(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + boolean aIsSaveDialog) { - return queryUserForFile(aLoaderInfo, parentComp, aTitleStr, (FileFilter)null, isSaveDialog); + return queryUserForFile(aLoaderInfo, aParentComp, aTitleStr, (FileFilter) null, aIsSaveDialog); } - + /** * Prompts the user to select multiple Files - * + * * @param aLoaderInfo - * : A object that stores the configuration from method call to method call. - * @param parentComp - * The parent component for the associated FileChooser GUI + * : A object that stores the configuration from method call to method call. + * @param aParentComp + * The parent component for the associated FileChooser GUI * @param aTitleStr - * The title of the FileChooser GUI - * @param ffList - * A List of FileFilters - * @param isSaveDialog - * Whether this FileChooser displays a GUI appropriate for saving a file + * The title of the FileChooser GUI + * @param aFileFilterC + * A List of FileFilters + * @param aIsSaveDialog + * Whether this FileChooser displays a GUI appropriate for saving a file * @return The selected file or null */ - public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, Collection ffList, boolean isSaveDialog) + public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + Collection aFileFilterC, boolean aIsSaveDialog) { - JFileChooser aFC; - List retList; - int aVal; - // Ensure we have a non null LoaderInfo if (aLoaderInfo == null) aLoaderInfo = new LoaderInfo(); // Set up the FileChooser - aFC = new StandardFileChooser(null); - aFC.setAcceptAllFileFilterUsed(false); - aFC.setDialogTitle(aTitleStr); - aFC.setMultiSelectionEnabled(true); - aLoaderInfo.loadConfig(aFC); + var tmpFC = new StandardFileChooser(null); + tmpFC.setAcceptAllFileFilterUsed(false); + tmpFC.setDialogTitle(aTitleStr); + tmpFC.setMultiSelectionEnabled(true); + aLoaderInfo.loadConfig(tmpFC); // Set in the FileFilters - for (FileFilter aFileFilter : ffList) - aFC.addChoosableFileFilter(aFileFilter); + for (FileFilter aFileFilter : aFileFilterC) + tmpFC.addChoosableFileFilter(aFileFilter); // Let the user choose a file - if (isSaveDialog == true) - aVal = aFC.showSaveDialog(parentComp); + int tmpVal; + if (aIsSaveDialog == true) + tmpVal = tmpFC.showSaveDialog(aParentComp); else - aVal = aFC.showOpenDialog(parentComp); + tmpVal = tmpFC.showOpenDialog(aParentComp); // Store off the current settings - aLoaderInfo.saveConfig(aFC); + aLoaderInfo.saveConfig(tmpFC); // Bail if no file chosen - if (aVal != JFileChooser.APPROVE_OPTION) + if (tmpVal != JFileChooser.APPROVE_OPTION) return null; // Return a list that is modifiable - retList = Arrays.asList(aFC.getSelectedFiles()); - retList = Lists.newArrayList(retList); - return retList; + var retFileL = Arrays.asList(tmpFC.getSelectedFiles()); + retFileL = new ArrayList<>(retFileL); + return retFileL; } - - public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, FileFilter aFileFilter, boolean isSaveDialog) + + public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + FileFilter aFileFilter, boolean isSaveDialog) { - List ffList; - - ffList = Lists.newArrayList(); + var tmpFileFilterL = new ArrayList(); if (aFileFilter != null) - ffList.add(aFileFilter); - - return queryUserForFiles(aLoaderInfo, parentComp, aTitleStr, ffList, isSaveDialog); + tmpFileFilterL.add(aFileFilter); + + return queryUserForFiles(aLoaderInfo, aParentComp, aTitleStr, tmpFileFilterL, isSaveDialog); } - - public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, boolean isSaveDialog) + + public static List queryUserForFiles(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, + boolean aIsSaveDialog) { - return queryUserForFiles(aLoaderInfo, parentComp, aTitleStr, (FileFilter)null, isSaveDialog); + return queryUserForFiles(aLoaderInfo, parentComp, aTitleStr, (FileFilter) null, aIsSaveDialog); } - + /** * Prompts the user to select a single folder - * + * * @param aLoaderInfo - * : A object that stores the configuration from method call to method call. - * @param parentComp - * The parent component for the associated FileChooser GUI + * : A object that stores the configuration from method call to method call. + * @param aParentComp + * The parent component for the associated FileChooser GUI * @param aTitleStr - * The title of the FileChooser GUI - * @param isSaveDialog - * Whether this FileChooser displays a GUI appropriate for saving a file + * The title of the FileChooser GUI + * @param aIsSaveDialog + * Whether this FileChooser displays a GUI appropriate for saving a file * @return The selected file or null */ - public static File queryUserForPath(LoaderInfo aLoaderInfo, Component parentComp, String aTitleStr, boolean isSaveDialog) + public static File queryUserForPath(LoaderInfo aLoaderInfo, Component aParentComp, String aTitleStr, + boolean aIsSaveDialog) { - JFileChooser aFC; - int aVal; - // Ensure we have a non null LoaderInfo if (aLoaderInfo == null) aLoaderInfo = new LoaderInfo(); // Set up the FileChooser - aFC = new StandardFileChooser(null); - aFC.setDialogTitle(aTitleStr); - aFC.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - aFC.setMultiSelectionEnabled(false); - - aLoaderInfo.loadConfig(aFC); + var tmpFC = new StandardFileChooser(null); + tmpFC.setDialogTitle(aTitleStr); + tmpFC.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + tmpFC.setMultiSelectionEnabled(false); + + aLoaderInfo.loadConfig(tmpFC); // Let the user choose a file - if (isSaveDialog == true) - aVal = aFC.showSaveDialog(parentComp); + int tmpVal; + if (aIsSaveDialog == true) + tmpVal = tmpFC.showSaveDialog(aParentComp); else - aVal = aFC.showOpenDialog(parentComp); + tmpVal = tmpFC.showOpenDialog(aParentComp); // Store off the current settings - aLoaderInfo.saveConfig(aFC); + aLoaderInfo.saveConfig(tmpFC); // Bail if no file chosen - if (aVal != JFileChooser.APPROVE_OPTION) + if (tmpVal != JFileChooser.APPROVE_OPTION) return null; // Return the file - return aFC.getSelectedFile(); + return tmpFC.getSelectedFile(); } - + } diff --git a/src/glum/io/LoaderInfo.java b/src/glum/io/LoaderInfo.java index 87969f7..4828551 100644 --- a/src/glum/io/LoaderInfo.java +++ b/src/glum/io/LoaderInfo.java @@ -1,8 +1,18 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; -import glum.zio.*; -import glum.zio.util.ZioUtil; - import java.awt.Dimension; import java.awt.Point; import java.io.File; @@ -12,36 +22,56 @@ import java.lang.reflect.Method; import javax.swing.JFileChooser; import javax.swing.plaf.FileChooserUI; +import glum.zio.*; +import glum.zio.util.ZioUtil; + /** - * Mutable object that contains the details needed to display a load or save GUI component. This object will typically be changed whenever the component is - * modified. + * Object that stores the configuration associated with a {@link JFileChooser}. + *

    + * The following state is stored: + *

      + *
    • Position of dialog + *
    • Size of dialog + *
    • File path + *
    + * + * @author lopeznr1 */ public class LoaderInfo implements ZioObj { + // State vars + private File path; + // Gui vars private boolean isVisible; private Point position; private Dimension dimension; - // Path vars - private String filePath; - - public LoaderInfo(File aFilePath) + /** Standard Constructor */ + public LoaderInfo(File aPath) { + path = aPath; + isVisible = false; position = null; dimension = null; - - filePath = null; - if (aFilePath != null) - filePath = aFilePath.getAbsolutePath(); } + /** Simplified Constructor */ public LoaderInfo() { this(null); } + // Accessor methods + // @formatter:off + /** Gets the file path. */ + public File getPath() { return path; } + + /** Sets the file path. */ + public void setPath(File aPath) { path = aPath; } + // @formatter:on + /** * Loads the current configuration into aFileChooser */ @@ -57,26 +87,23 @@ public class LoaderInfo implements ZioObj } // Bail if there is no filePath - if (filePath == null) + if (path == null) return; if (aFileChooser.getFileSelectionMode() == JFileChooser.DIRECTORIES_ONLY) { - File tmpFile = new File(filePath); - // Locate the first folder that exists -// File workPath = tmpFile.getParentFile(); - File workPath = tmpFile; - while (workPath != null && workPath.isDirectory() == false) - workPath = workPath.getParentFile(); + File tmpPath = path; + while (tmpPath != null && tmpPath.isDirectory() == false) + tmpPath = tmpPath.getParentFile(); // Set the FileChooser to the first folder that exists - aFileChooser.setCurrentDirectory(workPath); + aFileChooser.setCurrentDirectory(tmpPath); // Set the FileChooser's name area to reflect the absolute path - if (workPath != null) + if (tmpPath != null) { - String absPathStr = tmpFile.getAbsolutePath(); + String absPathStr = path.getAbsolutePath(); try { FileChooserUI tmpFCUI = aFileChooser.getUI(); @@ -84,7 +111,7 @@ public class LoaderInfo implements ZioObj Method setFileName = fcClass.getMethod("setFileName", String.class); setFileName.invoke(tmpFCUI, absPathStr); } - catch(Exception aExp) + catch (Exception aExp) { aExp.printStackTrace(); } @@ -92,15 +119,15 @@ public class LoaderInfo implements ZioObj } else { - File tmpPath = new File(filePath); - if (tmpPath.isFile() == true) + if (path.isFile() == true) { - aFileChooser.setSelectedFile(tmpPath); + aFileChooser.setSelectedFile(path); } else { // Set the FileChooser's current directory to the first folder that exists - while (tmpPath != null && tmpPath.getParentFile().isDirectory() == false) + File tmpPath = path; + while (tmpPath != null && tmpPath.getParentFile() != null && tmpPath.getParentFile().isDirectory() == false) tmpPath = tmpPath.getParentFile(); aFileChooser.setCurrentDirectory(tmpPath); @@ -117,15 +144,7 @@ public class LoaderInfo implements ZioObj dimension = aFileChooser.getSize(); isVisible = aFileChooser.isVisible(); - filePath = aFileChooser.getCurrentDirectory().getAbsolutePath(); - } - - /** - * Sets the filePath of the LoaderInfo. - */ - public void setFilePath(String aFilePath) - { - filePath = aFilePath; + path = aFileChooser.getCurrentDirectory(); } @Override @@ -134,11 +153,13 @@ public class LoaderInfo implements ZioObj aStream.readVersion(0); isVisible = aStream.readBool(); - position = ZioUtil.readPoint(aStream); dimension = ZioUtil.readDimension(aStream); - filePath = aStream.readString(); + String pathStr = aStream.readString(); + path = null; + if (pathStr != null) + path = new File(pathStr); } @Override @@ -147,11 +168,13 @@ public class LoaderInfo implements ZioObj aStream.writeVersion(0); aStream.writeBool(isVisible); - ZioUtil.writePoint(aStream, position); ZioUtil.writeDimension(aStream, dimension); - aStream.writeString(filePath); + String pathStr = null; + if (path != null) + pathStr = path.getAbsolutePath(); + aStream.writeString(pathStr); } } diff --git a/src/glum/io/NullOutputStream.java b/src/glum/io/NullOutputStream.java index 4047c23..9e85f81 100644 --- a/src/glum/io/NullOutputStream.java +++ b/src/glum/io/NullOutputStream.java @@ -1,15 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; import java.io.DataOutputStream; import java.io.IOException; +/** + * Implementation of {@link DataOutputStream} useful for counting bytes written. + * + * @author lopeznr1 + */ public class NullOutputStream extends DataOutputStream { - protected int byteCount; + private int byteCount; - /** - * OutputStream used to count bytes that are to be written. - */ + /** Standard Constructor */ public NullOutputStream() { super(null); diff --git a/src/glum/io/ParseUtil.java b/src/glum/io/ParseUtil.java new file mode 100644 index 0000000..cd6b710 --- /dev/null +++ b/src/glum/io/ParseUtil.java @@ -0,0 +1,148 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.io; + +/** + * Collection of utility methods for parsing values from text input. + * + * @author lopeznr1 + */ +public class ParseUtil +{ + /** + * Reads a boolean from a string with out throwing a exception. + */ + public static boolean readBoolean(String aStr, boolean aVal) + { + if (aStr == null) + return aVal; + + // Special case for 1 char strings + if (aStr.length() == 1) + { + char aChar; + + aChar = aStr.charAt(0); + if (aChar == 'T' || aChar == 't' || aChar == '1') + return true; + + return false; + } + + try + { + return Boolean.valueOf(aStr).booleanValue(); + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Reads a double from a string with out throwing a exception. Note aStr can have an number of separators: comma + * chars + */ + public static double readDouble(String aStr, double aVal) + { + try + { + aStr = aStr.replace(",", ""); + return Double.parseDouble(aStr); + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Reads a float from a string with out throwing a exception. Note aStr can have an number of separators: comma chars + */ + public static float readFloat(String aStr, float aVal) + { + try + { + aStr = aStr.replace(",", ""); + return Float.parseFloat(aStr); + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Reads an int from a string without throwing a exception. Note aStr can have an number of separators: comma chars + */ + public static int readInt(String aStr, int aVal) + { + try + { + aStr = aStr.replace(",", ""); + return Integer.parseInt(aStr); + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Reads a long from a string without throwing a exception. Note aStr can have an number of separators: comma chars + */ + public static long readLong(String aStr, long aVal) + { + try + { + aStr = aStr.replace(",", ""); + return Long.parseLong(aStr); + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Reads an int (forced to fit within a range) from a string with out throwing a exception. + */ + public static int readRangeInt(String aStr, int aMinVal, int aMaxVal, int aVal) + { + try + { + int tmpInt = Integer.parseInt(aStr); + if (tmpInt < aMinVal) + tmpInt = aMinVal; + else if (tmpInt > aMaxVal) + tmpInt = aMaxVal; + + return tmpInt; + } + catch (Exception aExp) + { + return aVal; + } + } + + /** + * Utility method to strip the white space from an array of tokens. + */ + public static void cleanTokens(String[] aTokenArr) + { + for (int c1 = 0; c1 < aTokenArr.length; c1++) + aTokenArr[c1] = aTokenArr[c1].strip(); + } + +} diff --git a/src/glum/io/RegExFileFilter.java b/src/glum/io/RegExFileFilter.java index 6aaeaf5..d4b30dc 100644 --- a/src/glum/io/RegExFileFilter.java +++ b/src/glum/io/RegExFileFilter.java @@ -1,9 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; import java.io.File; import java.util.regex.Pattern; + import javax.swing.filechooser.FileFilter; +/** + * Implementation of {@link FileFilter} used to gather files that match a regular expression. + * + * @author lopeznr1 + */ public class RegExFileFilter extends FileFilter implements java.io.FileFilter { // State vars @@ -11,6 +30,16 @@ public class RegExFileFilter extends FileFilter implements java.io.FileFilter private Pattern matchPattern; private boolean allowDirs; + /** + * Standard Constructor + * + * @param aDescription + * Textual description of this filter. + * @param aMatchRegEx + * Regular expression used for matching. + * @param aAllowDirs + * Set to true if directories should (always) be kept or rejected. + */ public RegExFileFilter(String aDescription, String aMatchRegEx, boolean aAllowDirs) { description = aDescription; @@ -18,6 +47,7 @@ public class RegExFileFilter extends FileFilter implements java.io.FileFilter allowDirs = aAllowDirs; } + /** Simplified Constructor */ public RegExFileFilter(String aDescription, String aMatchRegEx) { this(aDescription, aMatchRegEx, true); @@ -34,15 +64,13 @@ public class RegExFileFilter extends FileFilter implements java.io.FileFilter @Override public boolean accept(File aFile) { - String fileName; - // Allow directories if appropriate if (aFile.isDirectory() == true) return allowDirs; // Retrieve the corresponding file name - fileName = aFile.getName(); - + var fileName = aFile.getName(); + // Test to see if the fileName matches the compiled regex if (matchPattern.matcher(fileName).matches() == true) return true; diff --git a/src/glum/io/SimpleFileFilter.java b/src/glum/io/SimpleFileFilter.java index b9f522a..bc5f044 100644 --- a/src/glum/io/SimpleFileFilter.java +++ b/src/glum/io/SimpleFileFilter.java @@ -1,23 +1,57 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; import java.io.File; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; + import javax.swing.filechooser.FileFilter; +/** + * Implementation of {@link FileFilter} used to gather files that have a matching file extension. + * + * @author lopeznr1 + */ public class SimpleFileFilter extends FileFilter implements java.io.FileFilter { // State vars - private Collection extensionList; + private ArrayList extensionL; private String description; private boolean allowDirs; + /** + * Standard Constructor + * + * @param aDescription + * Textual description of this filter. + */ public SimpleFileFilter(String aDescription) { - allowDirs = true; + extensionL = new ArrayList(); description = aDescription; - extensionList = new LinkedList(); + allowDirs = true; } + /** + * Convenience Constructor + * + * @param aDescription + * Textual description of this filter. + * @param aExtension + * A file extension to match against. + */ public SimpleFileFilter(String aDescription, String aExtension) { this(aDescription); @@ -30,15 +64,24 @@ public class SimpleFileFilter extends FileFilter implements java.io.FileFilter public void addExtension(String aExtension) { if (aExtension != null) - extensionList.add(aExtension); + extensionL.add(aExtension); } - + /** * Adds the collections of file extensions which should be allowed through this filter */ - public void addExtensions(String... extArr) + public void addExtensions(String... aExtensionArr) { - for (String aExtension : extArr) + for (String aExtension : aExtensionArr) + addExtension(aExtension); + } + + /** + * Adds the collections of file extensions which should be allowed through this filter + */ + public void addExtensions(Collection aExtensionC) + { + for (String aExtension : aExtensionC) addExtension(aExtension); } @@ -53,28 +96,25 @@ public class SimpleFileFilter extends FileFilter implements java.io.FileFilter @Override public boolean accept(File aFile) { - String aStr, aFileName; - int aIndex; - // Allow directories if appropriate if (aFile.isDirectory() == true) return allowDirs; // Retrieve the corresponding file name - aFileName = aFile.getName(); + var tmpFileName = aFile.getName(); // Ensure the file has an extension - aIndex = aFileName.lastIndexOf('.'); - if (aIndex == -1) + var tmpIdx = tmpFileName.lastIndexOf('.'); + if (tmpIdx == -1) return false; // See if aFileName's extension matches any in extensionList - for (String aExt : extensionList) + for (String aExt : extensionL) { - if (aFileName.length() > aExt.length()) + if (tmpFileName.length() > aExt.length()) { - aStr = aFileName.substring(aFileName.length() - aExt.length()); - if (aExt.equalsIgnoreCase(aStr) == true) + var tmpStr = tmpFileName.substring(tmpFileName.length() - aExt.length()); + if (aExt.equalsIgnoreCase(tmpStr) == true) return true; } } diff --git a/src/glum/io/StandardFileChooser.java b/src/glum/io/StandardFileChooser.java index 2340c54..31fcb2d 100644 --- a/src/glum/io/StandardFileChooser.java +++ b/src/glum/io/StandardFileChooser.java @@ -1,8 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; import java.awt.*; -import javax.swing.*; +import javax.swing.JDialog; +import javax.swing.JFileChooser; + +/** + * Enhanced {@link JFileChooser} that allows external code to position the file dialog. + * + * @author lopeznr1 + */ public class StandardFileChooser extends JFileChooser { // Gui vars diff --git a/src/glum/io/TokenProcessor.java b/src/glum/io/TokenProcessor.java index 3d1dc75..0d86005 100644 --- a/src/glum/io/TokenProcessor.java +++ b/src/glum/io/TokenProcessor.java @@ -1,18 +1,35 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; +/** + * Interface that provides a mechanism to allow the processing of an array of (string) tokens. + * + * @author lopeznr1 + */ public interface TokenProcessor { /** - * Lets the processor know that it will not be called anymore. This method is - * only called after all data has been read in from the corresponding stream. - * Note this method will never get called if the reading was aborted or an IO - * exception occured. + * Lets the processor know that it will not be called anymore. This method is only called after all data has been + * read in from the corresponding stream. Note this method will never get called if the reading was aborted or an IO + * exception occurred. */ public void flush(); /** * Returns true if able to handle the tokens */ - public boolean process(String[] tokens, int lineNum); + public boolean process(String[] aTokenArr, int aLineNum); } diff --git a/src/glum/io/WarningTP.java b/src/glum/io/WarningTP.java index a46255d..8258559 100644 --- a/src/glum/io/WarningTP.java +++ b/src/glum/io/WarningTP.java @@ -1,30 +1,51 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io; -import java.util.*; +import java.util.LinkedHashMap; +import java.util.Map; +/** + * Implementation of {@link TokenProcessor} used to log (to stdout) warnings for tokens where the first token matches a + * specific value. + * + * @author lopeznr1 + */ public class WarningTP implements TokenProcessor { - private Map warningSet; + // State vars + private Map warningM; - /** - * Constructor - */ + /** Standard Constructor */ public WarningTP() { - warningSet = new LinkedHashMap(); + warningM = new LinkedHashMap(); } /** - * add - Adds a new warning to the set of warnings; Note if aMsg is null - * then aInstr will just be ignored - */ + * Adds a new warning to the set of warnings. If during the processing of tokens the first token equals aInstr then + * the warning message will be logged to stdout. + *

    + * Note if aMsg is null then aInstr will just be ignored + */ public void add(String aInstr, String aMsg) { // Insanity check if (aInstr == null) return; - warningSet.put(aInstr, aMsg); + warningM.put(aInstr, aMsg); } @Override @@ -34,20 +55,18 @@ public class WarningTP implements TokenProcessor } @Override - public boolean process(String[] tokens, int lineNum) + public boolean process(String[] aTokenArr, int aLineNum) { // Insanity check - if (tokens == null) + if (aTokenArr == null) return false; - if (warningSet.containsKey(tokens[0]) == true) + if (warningM.containsKey(aTokenArr[0]) == true) { - String aMsg; - // Display the aMsg if associated with aInstr - aMsg = warningSet.get(tokens[0]); - if (aMsg instanceof String) - System.out.println("[" + lineNum + "] " + aMsg); + var tmpMsg = warningM.get(aTokenArr[0]); + if (tmpMsg instanceof String) + System.out.println("[" + aLineNum + "] " + tmpMsg); return true; } diff --git a/src/glum/io/token/BaseTokenizer.java b/src/glum/io/token/BaseTokenizer.java index d7e2fc2..e6b9996 100644 --- a/src/glum/io/token/BaseTokenizer.java +++ b/src/glum/io/token/BaseTokenizer.java @@ -1,5 +1,23 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io.token; +/** + * Abstract base {@link Tokenizer}. + * + * @author lopeznr1 + */ public abstract class BaseTokenizer implements Tokenizer { // Constants @@ -10,6 +28,7 @@ public abstract class BaseTokenizer implements Tokenizer // State vars protected int commentMode; + /** Standard Constructor */ public BaseTokenizer() { commentMode = MODE_ANY_POS; @@ -26,31 +45,31 @@ public abstract class BaseTokenizer implements Tokenizer /** * Returns the string with the comments stripped based on the allowable comment style */ - protected String getCleanString(String inputStr) + protected String getCleanString(String aInputStr) { if (commentMode == MODE_NONE) { - return inputStr; + return aInputStr; } else if (commentMode == MODE_FIRST_POS) { - if (inputStr.indexOf(0) == '#') - return ""; + if (aInputStr.indexOf(0) == '#') + return ""; } else if (commentMode == MODE_ANY_POS) { int index; - index = inputStr.indexOf('#'); + index = aInputStr.indexOf('#'); if (index != -1) - inputStr = inputStr.substring(0, index); + aInputStr = aInputStr.substring(0, index); } else { throw new RuntimeException("Error: commentMode:" + commentMode); } - - return inputStr; + + return aInputStr; } } diff --git a/src/glum/io/token/MatchTokenizer.java b/src/glum/io/token/MatchTokenizer.java index 8dfa1b2..98c642e 100644 --- a/src/glum/io/token/MatchTokenizer.java +++ b/src/glum/io/token/MatchTokenizer.java @@ -1,13 +1,39 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io.token; -import java.util.*; -import java.util.regex.*; +import java.util.ArrayList; +import java.util.regex.Pattern; +/** + * Implementation of {@link BaseTokenizer} that transforms an input string into tokens by matching against a regular + * expression. + * + * @author lopeznr1 + */ public class MatchTokenizer extends BaseTokenizer { + // State vars private Pattern myPattern; private boolean autoStripQuotes; + /** + * Standard Constructor + * + * @param aRegEx + * The regular expression used to tokenize input strings. + */ public MatchTokenizer(String aRegEx) { // Compile the pattern @@ -25,32 +51,28 @@ public class MatchTokenizer extends BaseTokenizer } @Override - public ArrayList getTokens(String inputStr) + public ArrayList getTokens(String aInputStr) { - Matcher aMatcher; - ArrayList aList; - String aMatch; - // Clean up the input string before processing - inputStr = getCleanString(inputStr); + aInputStr = getCleanString(aInputStr); - aMatcher = myPattern.matcher(inputStr); - if (aMatcher == null) + var tmpMatcher = myPattern.matcher(aInputStr); + if (tmpMatcher == null) return null; - aList = new ArrayList(); - while (aMatcher.find() == true) + var retTokenL = new ArrayList(); + while (tmpMatcher.find() == true) { - aMatch = aMatcher.group(); + var tmpMatch = tmpMatcher.group(); // Strip the (double) quotes if requested if (autoStripQuotes == true) - aMatch = TokenUtil.getRawStr(aMatch); + tmpMatch = TokenUtil.getRawStr(tmpMatch); - aList.add(aMatch); + retTokenL.add(tmpMatch); } - return aList; + return retTokenL; } } diff --git a/src/glum/io/token/SplitTokenizer.java b/src/glum/io/token/SplitTokenizer.java index 9470cca..3610b2b 100644 --- a/src/glum/io/token/SplitTokenizer.java +++ b/src/glum/io/token/SplitTokenizer.java @@ -1,14 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io.token; -import java.util.*; -import java.util.regex.*; - -import com.google.common.collect.Lists; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Pattern; +/** + * Implementation of {@link BaseTokenizer} that transforms an input string into tokens by splitting against a regular + * expression. + * + * @author lopeznr1 + */ public class SplitTokenizer extends BaseTokenizer { - private Pattern myPattern; + // Attributes + private final Pattern myPattern; + /** Standard Construcotr */ public SplitTokenizer(String aRegEx) { // Compile the pattern @@ -16,18 +36,15 @@ public class SplitTokenizer extends BaseTokenizer } @Override - public ArrayList getTokens(String inputStr) + public ArrayList getTokens(String aInputStr) { - String[] tokenArr; - ArrayList retList; - // Clean up the input string before processing - inputStr = getCleanString(inputStr); + aInputStr = getCleanString(aInputStr); - tokenArr = myPattern.split(inputStr, -1); + var tokenArr = myPattern.split(aInputStr, -1); - retList = Lists.newArrayList(tokenArr); - return retList; + var retTokenL = new ArrayList<>(Arrays.asList(tokenArr)); + return retTokenL; } } diff --git a/src/glum/io/token/TokenUtil.java b/src/glum/io/token/TokenUtil.java index b521854..1074023 100644 --- a/src/glum/io/token/TokenUtil.java +++ b/src/glum/io/token/TokenUtil.java @@ -1,5 +1,10 @@ package glum.io.token; +/** + * Collection of utility method to aid with tokenization of input strings. + * + * @author lopeznr1 + */ public class TokenUtil { /** @@ -24,29 +29,26 @@ public class TokenUtil } /** - * Utility method to convert a wild card exression to a regular - * expression. Currently only the special chars '?', '*' are supported. - * Source: http://www.rgagnon.com/javadetails/java-0515.html + * Utility method to convert a wild card exression to a regular expression. Currently only the special chars '?', '*' + * are supported. Source: http://www.rgagnon.com/javadetails/java-0515.html */ - public static String convertWildCardToRegEx(String wildcard) + public static String convertWildCardToRegEx(String aWildcard) { - StringBuffer regex; - - regex = new StringBuffer(wildcard.length()); - regex.append('^'); - for (int i = 0, is = wildcard.length(); i < is; i++) + var regexSB = new StringBuffer(aWildcard.length()); + regexSB.append('^'); + for (int i = 0, is = aWildcard.length(); i < is; i++) { - char c = wildcard.charAt(i); + char c = aWildcard.charAt(i); switch (c) { case '*': - regex.append(".*"); - break; - + regexSB.append(".*"); + break; + case '?': - regex.append("."); - break; - + regexSB.append("."); + break; + // escape special regexp-characters case '(': case ')': @@ -59,18 +61,18 @@ public class TokenUtil case '}': case '|': case '\\': - regex.append("\\"); - regex.append(c); - break; - + regexSB.append("\\"); + regexSB.append(c); + break; + default: - regex.append(c); - break; + regexSB.append(c); + break; } } - - regex.append('$'); - return (regex.toString()); - } + + regexSB.append('$'); + return (regexSB.toString()); + } } diff --git a/src/glum/io/token/Tokenizer.java b/src/glum/io/token/Tokenizer.java index 10150eb..690ed17 100644 --- a/src/glum/io/token/Tokenizer.java +++ b/src/glum/io/token/Tokenizer.java @@ -1,12 +1,30 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.io.token; import java.util.ArrayList; +/** + * Interface that provide a mechanism for transforming an input string into an array of (string) tokens. + * + * @author lopeznr1 + */ public interface Tokenizer { /** * Returns all the tokens that match the pattern from the input */ - public ArrayList getTokens(String inputStr); + public ArrayList getTokens(String aInputStr); } diff --git a/src/glum/item/BaseItemManager.java b/src/glum/item/BaseItemManager.java new file mode 100644 index 0000000..5fee8cd --- /dev/null +++ b/src/glum/item/BaseItemManager.java @@ -0,0 +1,133 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Sets; + +/** + * Base implementation of the ItemManager interface. + * + * @author lopeznr1 + */ +public class BaseItemManager implements ItemManager +{ + // State vars + private List listenerL; + + private ImmutableList fullItemL; + private ImmutableSet pickItemS; + + /** + * Standard Constructor + */ + public BaseItemManager() + { + listenerL = new ArrayList<>(); + + fullItemL = ImmutableList.of(); + pickItemS = ImmutableSet.of(); + } + + @Override + public void addListener(ItemEventListener aListener) + { + listenerL.add(aListener); + } + + @Override + public void delListener(ItemEventListener aListener) + { + listenerL.remove(aListener); + } + + @Override + public ImmutableList getAllItems() + { + return fullItemL; + } + + @Override + public int getNumItems() + { + return fullItemL.size(); + } + + @Override + public ImmutableSet getSelectedItems() + { + return pickItemS; + } + + @Override + public void removeItems(Collection aItemC) + { + List tmpTrackL = new ArrayList<>(fullItemL); + tmpTrackL.removeAll(aItemC); + fullItemL = ImmutableList.copyOf(tmpTrackL); + + Set tmpTrackS = new LinkedHashSet<>(pickItemS); + tmpTrackS.removeAll(aItemC); + pickItemS = ImmutableSet.copyOf(tmpTrackS); + + // Send out the appropriate notifications + notifyListeners(this, ItemEventType.ItemsChanged); + notifyListeners(this, ItemEventType.ItemsSelected); + } + + @Override + public void setAllItems(Collection aItemC) + { + fullItemL = ImmutableList.copyOf(aItemC); + + // Update the picked items to contain items only in fullItemL + Set tmpS = new LinkedHashSet<>(fullItemL); + tmpS = Sets.intersection(pickItemS, tmpS); + pickItemS = ImmutableSet.copyOf(tmpS); + + notifyListeners(this, ItemEventType.ItemsChanged); + notifyListeners(this, ItemEventType.ItemsSelected); + } + + @Override + public void setSelectedItems(Collection aItemC) + { + // Bail if the selection has not changed + if (aItemC.equals(pickItemS.asList()) == true) + return; + + // Update our selection + pickItemS = ImmutableSet.copyOf(aItemC); + + // Send out the appropriate notifications + notifyListeners(this, ItemEventType.ItemsSelected); + } + + /** + * Sends out notification to all the listeners of the specified event. + */ + protected void notifyListeners(Object aSource, ItemEventType aEventType) + { + for (ItemEventListener aListener : listenerL) + aListener.handleItemEvent(aSource, aEventType); + } + +} diff --git a/src/glum/item/ConstIdGenerator.java b/src/glum/item/ConstIdGenerator.java new file mode 100644 index 0000000..0ae611e --- /dev/null +++ b/src/glum/item/ConstIdGenerator.java @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * {@link IdGenerator} that always returns the exact same value. + * + * @author lopeznr1 + */ +public class ConstIdGenerator implements IdGenerator +{ + // State vars + private int constId; + + /** Standard Constructor */ + public ConstIdGenerator(int aConstId) + { + constId = aConstId; + } + + @Override + public int getNextId() + { + return constId; + } + +} diff --git a/src/glum/item/IdGenerator.java b/src/glum/item/IdGenerator.java new file mode 100644 index 0000000..732f875 --- /dev/null +++ b/src/glum/item/IdGenerator.java @@ -0,0 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * Interface that provides a mechanism for generating ids. + * + * @author lopeznr1 + */ +public interface IdGenerator +{ + /** + * Returns the next available id. The returned id should be unique from any + * other returned value. + */ + public int getNextId(); + +} diff --git a/src/glum/item/IncrIdGenerator.java b/src/glum/item/IncrIdGenerator.java new file mode 100644 index 0000000..eaa2bc7 --- /dev/null +++ b/src/glum/item/IncrIdGenerator.java @@ -0,0 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * {@link IdGenerator} which returns incrementing id values. + * + * @author lopeznr1 + */ +public class IncrIdGenerator implements IdGenerator +{ + private int nextId; + + /** + * Standard Constructor + * + * @param aBegId + * The id of the first value to be returned. + */ + public IncrIdGenerator(int aBegId) + { + nextId = aBegId; + } + + @Override + public int getNextId() + { + int retId = nextId; + nextId++; + + return retId; + } + +} diff --git a/src/glum/item/ItemEventListener.java b/src/glum/item/ItemEventListener.java new file mode 100644 index 0000000..ee4f692 --- /dev/null +++ b/src/glum/item/ItemEventListener.java @@ -0,0 +1,33 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * Interface that provides for the callback mechanism for notification of item changes. + * + * @author lopeznr1 + */ +public interface ItemEventListener +{ + /** + * Notification method that the state of items has changed + * + * @param aSource + * The object that generated this event. + * @param aEvent + * The type that describes the event. + */ + public void handleItemEvent(Object aSource, ItemEventType aEventType); + +} diff --git a/src/glum/item/ItemEventType.java b/src/glum/item/ItemEventType.java new file mode 100644 index 0000000..82e51cb --- /dev/null +++ b/src/glum/item/ItemEventType.java @@ -0,0 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * Enumeration that provides for the various event types that are supported. + * + * @author lopeznr1 + */ +public enum ItemEventType +{ + /** + * Type which corresponds to an event where the list of items have changed. + */ + ItemsChanged, + + /** + * Type which corresponds to an event where any items have been mutated. + */ + ItemsMutated, + + /** + * Type which corresponds to an event where the selected items have changed. + */ + ItemsSelected; + +} diff --git a/src/glum/item/ItemGroup.java b/src/glum/item/ItemGroup.java new file mode 100644 index 0000000..e5594f7 --- /dev/null +++ b/src/glum/item/ItemGroup.java @@ -0,0 +1,42 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +/** + * Enum which defines the group of items to apply an operation to. + * + * @author lopeznr1 + */ +public enum ItemGroup +{ + /** + * Type which corresponds to no items. + */ + None, + + /** + * Type which corresponds to all available items. + */ + All, + + /** + * Type which corresponds to the selected items. + */ + Selected, + + /** + * Type which corresponds to the visible items. + */ + Visible, +} diff --git a/src/glum/item/ItemManager.java b/src/glum/item/ItemManager.java new file mode 100644 index 0000000..ebdd170 --- /dev/null +++ b/src/glum/item/ItemManager.java @@ -0,0 +1,60 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +import java.util.Collection; + +import com.google.common.collect.ImmutableSet; + +import glum.gui.panel.itemList.ItemProcessor; + +/** + * Interface that defines a collection of methods used to manage and handle notification of a set of items. + * + * @author lopeznr1 + */ +public interface ItemManager extends ItemProcessor +{ + /** + * Returns the set of selected items. + */ + public ImmutableSet getSelectedItems(); + + /** + * Removes the specified lists of items from this {@link ItemManager}. + * + * @param aItemC + * The list of items to be removed. + */ + public void removeItems(Collection aItemC); + + /** + * Installs the specified items into this {@link ItemManager}. + *

    + * All prior items will be cleared out. + * + * @param aItemC + * The list of items to be installed. + */ + public void setAllItems(Collection aItemC); + + /** + * Method that sets in the list of selected items. + * + * @param aItemC + * The list of items to be selected. + */ + public void setSelectedItems(Collection aItemC); + +} diff --git a/src/glum/item/ItemManagerUtil.java b/src/glum/item/ItemManagerUtil.java new file mode 100644 index 0000000..5eeac65 --- /dev/null +++ b/src/glum/item/ItemManagerUtil.java @@ -0,0 +1,55 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.item; + +import java.util.Set; + +import com.google.common.collect.*; + +/** + * Collection of utility methods for working with ItemManagers. + * + * @author lopeznr1 + */ +public class ItemManagerUtil +{ + /** + * Utility method that will select all of the items in the specified ItemManager. + */ + public static void selectAll(ItemManager aManager) + { + aManager.setSelectedItems(aManager.getAllItems()); + } + + /** + * Utility method that will invert the selection of the specified ItemManager. + */ + public static void selectInvert(ItemManager aManager) + { + Set fullS = ImmutableSet.copyOf(aManager.getAllItems()); + Set pickS = ImmutableSet.copyOf(aManager.getSelectedItems()); + + Set tmpS = Sets.difference(fullS, pickS); + aManager.setSelectedItems(ImmutableList.copyOf(tmpS)); + } + + /** + * Utility method that will clear the selection of the specified ItemManager. + */ + public static void selectNone(ItemManager aManager) + { + aManager.setSelectedItems(ImmutableList.of()); + } + +} diff --git a/src/glum/logic/LogicChunk.java b/src/glum/logic/LogicChunk.java index f42e4b3..3efecca 100644 --- a/src/glum/logic/LogicChunk.java +++ b/src/glum/logic/LogicChunk.java @@ -1,13 +1,32 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic; +/** + * Interface that defines a specific block of logic. + * + * @author lopeznr1 + */ public interface LogicChunk { /** - * LogicChunk interface methods - */ + * Runs the logic asssociated with this {@link LogicChunk}. + */ public void activate(); - public void dispose(); - public String getName(); - public String getVersion(); + /** + * Notifies the {@link LogicChunk} that it will no longer be utilized. + */ + public void dispose(); } diff --git a/src/glum/logic/LogicChunkEngine.java b/src/glum/logic/LogicChunkEngine.java index 14613f4..2365467 100644 --- a/src/glum/logic/LogicChunkEngine.java +++ b/src/glum/logic/LogicChunkEngine.java @@ -1,37 +1,55 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic; -import glum.io.token.MatchTokenizer; -import glum.reflect.ReflectUtil; -import glum.registry.Registry; - import java.awt.GraphicsEnvironment; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.*; -import java.lang.reflect.Constructor; import java.net.URL; import java.util.*; import javax.swing.*; +import glum.io.token.MatchTokenizer; +import glum.reflect.ReflectUtil; +import glum.registry.Registry; + +/** + * Mechanism for loading {@link LogicChunk}s into an application. + * + * @author lopeznr1 + */ public class LogicChunkEngine implements ActionListener { + // Ref vars + private final Registry refRegistry; + // State vars - private Registry refRegistry; private JMenuBar menuBar; private Map menuMap; private ShutdownHook shutdownHook; + /** Standard Constructor */ public LogicChunkEngine(Registry aRegistry, URL aURL, String aAppName) { - boolean isHeadless; - refRegistry = aRegistry; menuMap = null; menuBar = null; // Are we headless - isHeadless = GraphicsEnvironment.isHeadless(); + var isHeadless = GraphicsEnvironment.isHeadless(); // Install our custom shutdown logic shutdownHook = new ShutdownHook(this, aAppName); @@ -53,7 +71,7 @@ public class LogicChunkEngine implements ActionListener if (aLogicChunk != null) aLogicChunk.dispose(); } - catch(Exception aExp) + catch (Exception aExp) { System.out.println("Failed to dispose LogicChunk. Exception:"); aExp.printStackTrace(); @@ -91,109 +109,85 @@ public class LogicChunkEngine implements ActionListener @Override public void actionPerformed(ActionEvent aEvent) { - LogicChunk aLogicChunk; - // Insanity check if (aEvent == null || menuMap == null) return; // Find the associated LogicChunk - aLogicChunk = (LogicChunk)menuMap.get(aEvent.getSource()); + var tmpLogicChunk = menuMap.get(aEvent.getSource()); // Activate the MenuItem - if (aLogicChunk != null) - aLogicChunk.activate(); + if (tmpLogicChunk != null) + tmpLogicChunk.activate(); } /** * Helper method to load up the LogicChunk described at aURL */ - private void loadLogicChunks(URL aURL, boolean isHeadless) + private void loadLogicChunks(URL aURL, boolean aIsHeadless) { - InputStream inStream; - BufferedReader br; - MatchTokenizer aTokenizer; - String regEx; - ArrayList tokenList; - String[] tokens; - int numTokens; - LinkedList currMenuList; - JMenuBar aMenuBar; - JMenu currMenu; - JMenuItem aMenuItem; - String strLine; - String aLoc; - LogicChunk logicChunk; + var aLoc = aURL.toString(); - aLoc = aURL.toString(); - - currMenuList = new LinkedList(); - aMenuItem = null; + var currMenuL = new LinkedList(); menuMap = new LinkedHashMap(); // Build our tokenizer - regEx = "(#.*)|([a-zA-Z0-9\\.]+)|(\"[^\"\\r\\n]*\")|([a-zA-Z0-9\\.]+)"; - aTokenizer = new MatchTokenizer(regEx); + var regEx = "(#.*)|([a-zA-Z0-9\\.]+)|(\"[^\"\\r\\n]*\")|([a-zA-Z0-9\\.]+)"; + var tmpTokenizer = new MatchTokenizer(regEx); // Create the MenuBar only if we are not headless - aMenuBar = null; - if (isHeadless == false) - aMenuBar = new JMenuBar(); + var tmpMenuBar = (JMenuBar) null; + if (aIsHeadless == false) + tmpMenuBar = new JMenuBar(); // Process our input - try + JMenu currMenu; + try (var br = new BufferedReader(new InputStreamReader(aURL.openStream()))) { - inStream = aURL.openStream(); - br = new BufferedReader(new InputStreamReader(inStream)); - // Read the lines while (true) { - strLine = br.readLine(); + var strLine = br.readLine(); if (strLine == null) { br.close(); - inStream.close(); break; } // Get the tokens out of strLine - tokenList = aTokenizer.getTokens(strLine); + var tokenL = tmpTokenizer.getTokens(strLine); - tokens = tokenList.toArray(new String[0]); - numTokens = tokens.length; + var tokenArr = tokenL.toArray(new String[0]); + var numTokens = tokenArr.length; + + // Skip to the next line if the line is empty + if (numTokens == 0) + continue; // Process the tokens - if (numTokens == 0) + if ((aIsHeadless == true) && (tokenArr[0].equals("Menu") == true || tokenArr[0].equals("MenuItem") == true + || tokenArr[0].equals("SubMenu") || tokenArr[0].equals("EndSubMenu"))) { - ; // Empty line + System.out.println("Ignoring:" + tokenArr[0] + " command. Running in headless environment."); + System.out.println("\tTokens: " + tokenArr); } - else if ((isHeadless == true) && (tokens[0].equals("Menu") == true || tokens[0].equals("MenuItem") == true || tokens[0].equals("SubMenu") || tokens[0].equals("EndSubMenu"))) + else if (tokenArr[0].equals("Menu") == true && numTokens == 2) { - System.out.println("Ignoring:" + tokens[0] + " command. Running in headless environment."); - System.out.println("\tTokens: " + tokens); - } - else if (tokens[0].equals("Menu") == true && numTokens == 2) - { - JMenu aMenu; - // Create a new menu - aMenu = new JMenu(tokens[1]); - aMenuBar.add(aMenu); + var tmpMenu = new JMenu(tokenArr[1]); + tmpMenuBar.add(tmpMenu); // Reset the menu list - currMenuList.clear(); - currMenuList.addLast(aMenu); + currMenuL.clear(); + currMenuL.addLast(tmpMenu); } - else if (tokens[0].equals("SubMenu") == true && numTokens == 2) + else if (tokenArr[0].equals("SubMenu") == true && numTokens == 2) { - JMenu aMenu; - // Get the current menu - if (currMenuList.isEmpty() == true) + if (currMenuL.isEmpty() == true) currMenu = null; else - currMenu = currMenuList.getLast(); + currMenu = currMenuL.getLast(); if (currMenu == null) { @@ -202,113 +196,108 @@ public class LogicChunkEngine implements ActionListener else { // Create and add into the current working menu - aMenu = new JMenu(tokens[1]); - currMenu.add(aMenu); + var tmpMenu = new JMenu(tokenArr[1]); + currMenu.add(tmpMenu); - currMenuList.addLast(aMenu); + currMenuL.addLast(tmpMenu); } } - else if (tokens[0].equals("EndSubMenu") == true && numTokens == 1) + else if (tokenArr[0].equals("EndSubMenu") == true && numTokens == 1) { - if (currMenuList.isEmpty() == true) + if (currMenuL.isEmpty() == true) System.out.println("[" + aLoc + "]: Warning no parent sub menu found. Instr: EndSubMenu\n"); else - currMenuList.removeLast(); + currMenuL.removeLast(); } // Process the various types of MenuItems - else if (tokens[0].equals("AutoItem") == true && numTokens == 3) + else if (tokenArr[0].equals("AutoItem") == true && numTokens == 3) { // Build the auto item and add it only into our MenuMap - logicChunk = loadLogicChunkInstance(refRegistry, tokens[2], tokens[1], aLoc); + var logicChunk = loadLogicChunkInstance(refRegistry, tokenArr[2], tokenArr[1], aLoc); if (logicChunk != null) - { menuMap.put(logicChunk, logicChunk); - } } - else if (tokens[0].equals("MenuItem") == true && (numTokens == 2 || numTokens == 3)) + else if (tokenArr[0].equals("MenuItem") == true && (numTokens == 2 || numTokens == 3)) { // Get the current menu - if (currMenuList.isEmpty() == true) + if (currMenuL.isEmpty() == true) currMenu = null; else - currMenu = currMenuList.getLast(); + currMenu = currMenuL.getLast(); if (currMenu == null) { System.out.println("[" + aLoc + "]: Warning no parent sub menu found. Instr: MenuItem.\n"); } - else if (tokens[1].equals("Break") == true) + else if (tokenArr[1].equals("Break") == true) { currMenu.addSeparator(); } else { + JMenuItem tmpMenuItem; + // Build the menu item if (numTokens == 2) { - aMenuItem = new JMenuItem(tokens[1]); - aMenuItem.setEnabled(false); + tmpMenuItem = new JMenuItem(tokenArr[1]); + tmpMenuItem.setEnabled(false); } else { // Try to build the LogicChunk and load it into our MenuMap - logicChunk = loadLogicChunkInstance(refRegistry, tokens[2], tokens[1], aLoc); + var logicChunk = loadLogicChunkInstance(refRegistry, tokenArr[2], tokenArr[1], aLoc); // Form the MenuItem or Menu if (logicChunk instanceof SubMenuChunk) - aMenuItem = new JMenu(tokens[1]); + tmpMenuItem = new JMenu(tokenArr[1]); else - aMenuItem = new JMenuItem(tokens[1]); - aMenuItem.addActionListener(this); + tmpMenuItem = new JMenuItem(tokenArr[1]); + tmpMenuItem.addActionListener(this); // Associate the MenuItem with the LogicChunk if (logicChunk != null) - menuMap.put(aMenuItem, logicChunk); + menuMap.put(tmpMenuItem, logicChunk); // Notify MenuItemChunk/SubMenuChunk of the associated menu item if (logicChunk instanceof MenuItemChunk) - ((MenuItemChunk)logicChunk).setMenuItem(aMenuItem); + ((MenuItemChunk) logicChunk).setMenuItem(tmpMenuItem); if (logicChunk instanceof SubMenuChunk) - ((SubMenuChunk)logicChunk).setMenu((JMenu)aMenuItem); + ((SubMenuChunk) logicChunk).setMenu((JMenu) tmpMenuItem); } - currMenu.add(aMenuItem); + currMenu.add(tmpMenuItem); } } else { - System.out.println("Unreconized line. Instruction: -" + tokens[0] + "-"); - System.out.println("\tTokens: " + tokens); + System.err.println("Unreconized line. Instruction: -" + tokenArr[0] + "-"); + System.err.println("\tTokens: " + tokenArr); } } } - catch(FileNotFoundException e) + catch (FileNotFoundException aExp) { - System.out.println("File not found: " + aLoc); + System.err.println("File not found: " + aLoc); return; } - catch(IOException e) + catch (IOException aExp) { - System.out.println("Ioexception occured in: LogicChunkEngine.loadLogicChunks()"); + System.err.println("Ioexception occured in: LogicChunkEngine.loadLogicChunks()"); return; } - menuBar = aMenuBar; + menuBar = tmpMenuBar; } /** * Attempts to load up the logicChunk with the specified aLabel. If that fails then will attempt to construct the * LogicChunk using the default constructor. */ - private static LogicChunk loadLogicChunkInstance(Registry aRegistry, String aFullClassPath, String aLabel, String aLoc) + private static LogicChunk loadLogicChunkInstance(Registry aRegistry, String aFullClassPath, String aLabel, + String aLoc) { - Class rawClass; - Constructor rawConstructor; - Class parmTypes1[] = {Registry.class, String.class}; - Class parmTypes2[] = {String.class}; - Object parmValues[]; - // Insanity check if (aFullClassPath == null) return null; @@ -316,7 +305,7 @@ public class LogicChunkEngine implements ActionListener try { // Retrieve the class and ensure it is a LogicChunk - rawClass = Class.forName(aFullClassPath); + var rawClass = Class.forName(aFullClassPath); if (LogicChunk.class.isAssignableFrom(rawClass) == false) { System.out.println("Failure: " + aFullClassPath + " is not a LogicChunk!"); @@ -325,38 +314,40 @@ public class LogicChunkEngine implements ActionListener } // Try the 1st preferred constructor - rawConstructor = ReflectUtil.getConstructorSafe(rawClass, parmTypes1); + Class parmTypes1[] = { Registry.class, String.class }; + var rawConstructor = ReflectUtil.getConstructorSafe(rawClass, parmTypes1); if (rawConstructor != null) { - parmValues = new Object[2]; + var parmValues = new Object[2]; parmValues[0] = aRegistry; parmValues[1] = aLabel; - return (LogicChunk)rawConstructor.newInstance(parmValues); + return (LogicChunk) rawConstructor.newInstance(parmValues); } // Try the 2nd preferred constructor + Class parmTypes2[] = { String.class }; rawConstructor = ReflectUtil.getConstructorSafe(rawClass, parmTypes2); if (rawConstructor != null) { - parmValues = new Object[1]; + var parmValues = new Object[1]; parmValues[0] = aLabel; - return (LogicChunk)rawConstructor.newInstance(parmValues); + return (LogicChunk) rawConstructor.newInstance(parmValues); } // Just use the default constructor else { - return (LogicChunk)rawClass.getDeclaredConstructor().newInstance(); + return (LogicChunk) rawClass.getDeclaredConstructor().newInstance(); } } - catch(ClassNotFoundException aExp) + catch (ClassNotFoundException aExp) { - System.out.println("Failure: " + aFullClassPath + " not found."); - System.out.println("\tLocation: " + aLoc + "\n"); + System.err.println("Failure: " + aFullClassPath + " not found."); + System.err.println("\tLocation: " + aLoc + "\n"); } - catch(Exception aExp) + catch (Exception aExp) { // Unknown Exception aExp.printStackTrace(); diff --git a/src/glum/logic/MenuItemChunk.java b/src/glum/logic/MenuItemChunk.java index 0f8fce7..8e5ac0c 100644 --- a/src/glum/logic/MenuItemChunk.java +++ b/src/glum/logic/MenuItemChunk.java @@ -1,11 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic; -import javax.swing.*; +import javax.swing.JMenuItem; +/** + * Interface used with {@link LogicChunk} to access the parent {@link JMenuItem}. + * + * @author lopeznr1 + */ public interface MenuItemChunk { /** - * MenuItemChunk interface methods + * Notifies the {@link LogicChunk} of the associated {@link JMenuItem} */ public void setMenuItem(JMenuItem aMI); diff --git a/src/glum/logic/ShutdownHook.java b/src/glum/logic/ShutdownHook.java index 63a4d50..67d1120 100644 --- a/src/glum/logic/ShutdownHook.java +++ b/src/glum/logic/ShutdownHook.java @@ -1,60 +1,75 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic; +import java.util.*; + import glum.util.ThreadUtil; -import java.util.List; -import java.util.Set; - -import com.google.common.collect.Lists; -import com.google.common.collect.Sets; - /** - * Shutdown logic that attempts to properly shutdown the refLogicChunkEngine. This logic assumes - * that any non daemon threads that appear after the creation of this shutdown hook are a direct - * result of the LogicChunkEngine. Any threads that are started due to a LogicChunkEngine should - * be properly stopped whenever the dispose() method is called on the corresponding LogicChunk. - * a - *

  • This shutdown hook should be installed via Runtime.getRuntime().addShutdownHook(). - *
  • This shutdown hook should be installed before aLogicChunkEngine.loadLogicChunks() is called. - *
  • This class should remain package visible + * Shutdown logic that attempts to properly shutdown the refLogicChunkEngine. This logic assumes that any non daemon + * threads that appear after the creation of this shutdown hook are a direct result of the LogicChunkEngine. Any threads + * that are started due to a LogicChunkEngine should be properly stopped whenever the dispose() method is called on the + * corresponding LogicChunk. + *
      + *
    • This shutdown hook should be installed via Runtime.getRuntime().addShutdownHook(). + *
    • This shutdown hook should be installed before aLogicChunkEngine.loadLogicChunks() is called. + *
    • This class should remain package visible + *
    + * + * @author lopeznr1 */ class ShutdownHook extends Thread { // State vars - private Set ignoreThreadSet; + private Set ignoreThreadS; private LogicChunkEngine refLogicChunkEngine; private String appName; private boolean doAbortExit; private boolean doQuickExit; - + + /** + * Standard Constructor + */ public ShutdownHook(LogicChunkEngine aLogicChunkEngine, String aAppName) { refLogicChunkEngine = aLogicChunkEngine; appName = aAppName; doAbortExit = false; doQuickExit = false; - + // Form the list of currently running non daemon threads that can be ignored // Any non daemon threads that appear are assumed to be started via a LogicChunk and thus // should be properly stopped whenever the dispose() method is called on the LogicChunk. - ignoreThreadSet = Sets.newHashSet(); - ignoreThreadSet.addAll(Thread.getAllStackTraces().keySet()); - ignoreThreadSet.add(this); - + ignoreThreadS = new HashSet<>(); + ignoreThreadS.addAll(Thread.getAllStackTraces().keySet()); + ignoreThreadS.add(this); + // Set in the preferred name of this thread setName("thread-" + getClass().getSimpleName()); } /** * Configures the shutdown hook to exit immediately by skipping the shutdown of the refLogicChunkEngine. - *

    + *

    * Note the associated LogicChunks dispose method will never be called. */ public void setAbortExit(boolean aBool) { doAbortExit = aBool; } - + /** * Configures the shutdown hook to exit quickly by not waiting for daemon threads. */ @@ -62,110 +77,105 @@ class ShutdownHook extends Thread { doQuickExit = aBool; } - + @Override public void run() { - List origList, currList; - ThreadGroup aThreadGroup; - int cntAttempts; - // Bail if we have been marked to abort if (doAbortExit == true) { System.out.println(appName + " has been aborted!"); return; } - + // Shutdown the LogicChunkEngine refLogicChunkEngine.dispose(); - + // Bail if we are configured to exit quickly if (doQuickExit == true) return; - + // Ensure all non daemon threads are dead - origList = Lists.newArrayList(); - currList = getBlockList(); - - cntAttempts = 1; - while (currList.isEmpty() == false) + List origL = new ArrayList<>(); + var currL = getBlockList(); + + var cntAttempts = 1; + while (currL.isEmpty() == false) { // Dump out the blocking threads, whenever there is a change - if (origList.size() != currList.size()) + if (origL.size() != currL.size()) { - System.out.println("Shutdown logic blocked by " + currList.size() + " threads.."); - for (Thread aThread : currList) + System.out.println("Shutdown logic blocked by " + currL.size() + " threads.."); + for (Thread aThread : currL) { - aThreadGroup = aThread.getThreadGroup(); - if (aThreadGroup != null) - System.out.println(" Waiting for non daemon thread to die: " + aThread.getName() + " threadGroup: " + aThreadGroup.getName()); + var tmpThreadGroup = aThread.getThreadGroup(); + if (tmpThreadGroup != null) + System.out.println(" Waiting for non daemon thread to die: " + aThread.getName() + + " threadGroup: " + tmpThreadGroup.getName()); else - System.out.println(" Waiting for non daemon thread to die: " + aThread.getName() + " threadGroup: null"); - + System.out.println( + " Waiting for non daemon thread to die: " + aThread.getName() + " threadGroup: null"); + } System.out.println(); - - origList = currList; + + origL = currL; } - + // Give the blocked threads some time to exit ThreadUtil.safeSleep(1000); // Retrieve the updated list of blocking threads - currList = getBlockList(); + currL = getBlockList(); // Bail if too many failed attempts - if (cntAttempts >= 7 & currList.isEmpty() == false) + if (cntAttempts >= 7 & currL.isEmpty() == false) { // Print out the stack trace of all active threads - System.out.println("Shutdown logic blocked by " + currList.size() + " threads.."); - for (Thread aThread : currList) + System.out.println("Shutdown logic blocked by " + currL.size() + " threads.."); + for (Thread aThread : currL) { System.out.println(" [" + aThread.getName() + "] StackTrace:"); for (StackTraceElement aItem : aThread.getStackTrace()) System.out.println(" " + aItem.toString()); } - + System.out.println("\nAborting " + appName + ". Waited too long..."); break; } - + cntAttempts++; } - + // Let the user know that we were properly terminated System.out.println(appName + " has been shutdown."); } - + /** - * Helper method to retrieve the set of non daemon threads that we are waiting on to finish executing - *
    Note any thread with the following name pattern will be ignored: + * Helper method to retrieve the set of non daemon threads that we are waiting on to finish executing + *

    + * Note any thread with the following name pattern will be ignored:
    * AWT-*, DestroyJavaVM */ private List getBlockList() { - List fullList; - List blockList; - String name; - - blockList = Lists.newLinkedList(); - - fullList = Lists.newLinkedList(Thread.getAllStackTraces().keySet()); - for (Thread aThread : fullList) + var blockL = new ArrayList(); + + var fullL = new ArrayList<>(Thread.getAllStackTraces().keySet()); + for (Thread aThread : fullL) { - name = aThread.getName(); - + var name = aThread.getName(); + // Record the non daemon threads that are still alive and not in the ignoreThreadSet or with a known name - if (aThread.isDaemon() == false && aThread.isAlive() == true && ignoreThreadSet.contains(aThread) == false) + if (aThread.isDaemon() == false && aThread.isAlive() == true && ignoreThreadS.contains(aThread) == false) { // Only add the thread if the name is not one of a well defined name such as: AWT-*, DestroyJavaVM if ((name.startsWith("AWT-") == true || name.startsWith("DestroyJavaVM") == true) == false) - blockList.add(aThread); + blockL.add(aThread); } } - - return blockList; + + return blockL; } } diff --git a/src/glum/logic/SubMenuChunk.java b/src/glum/logic/SubMenuChunk.java index d2f2b54..618e15c 100644 --- a/src/glum/logic/SubMenuChunk.java +++ b/src/glum/logic/SubMenuChunk.java @@ -1,12 +1,30 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic; -import javax.swing.*; +import javax.swing.JMenu; +/** + * Interface used with {@link LogicChunk} to access the parent {@link JMenu}. + * + * @author lopeznr1 + */ public interface SubMenuChunk { /** - * MenuItemChunk interface methods - */ + * Notifies the {@link LogicChunk} of the associated {@link JMenu} + */ public void setMenu(JMenu aMenu); } diff --git a/src/glum/logic/dock/FrontendLoadMI.java b/src/glum/logic/dock/FrontendLoadMI.java index 50c5d8a..32ff5fb 100644 --- a/src/glum/logic/dock/FrontendLoadMI.java +++ b/src/glum/logic/dock/FrontendLoadMI.java @@ -1,24 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.dock; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.util.Set; + import javax.swing.JMenu; import javax.swing.JMenuItem; -import glum.logic.LogicChunk; -import glum.logic.SubMenuChunk; -import glum.registry.Registry; - import bibliothek.gui.DockFrontend; import bibliothek.gui.Dockable; import bibliothek.gui.dock.event.DockFrontendListener; +import glum.logic.LogicChunk; +import glum.logic.SubMenuChunk; +import glum.registry.Registry; +/** + * {@link LogicChunk} used to allow an end user to load a dockable configuration. + * + * @author lopeznr1 + */ public class FrontendLoadMI implements LogicChunk, SubMenuChunk, DockFrontendListener, ActionListener { - protected DockFrontend refFrontend; - protected JMenu refMenu; + // Ref vars + private final DockFrontend refFrontend; + // State vars + private JMenu refMenu; + + /** Standard Constructor */ public FrontendLoadMI(Registry aRegistry, String aLabel) { refFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); @@ -28,14 +49,11 @@ public class FrontendLoadMI implements LogicChunk, SubMenuChunk, DockFrontendLis @Override public void actionPerformed(ActionEvent aEvent) { - Object aSource; - String aName; - - aSource = aEvent.getSource(); - if (aSource instanceof JMenuItem) + var source = aEvent.getSource(); + if (source instanceof JMenuItem) { - aName = ((JMenuItem)aSource).getText(); - refFrontend.load(aName); + var name = ((JMenuItem) source).getText(); + refFrontend.load(name); } } @@ -50,18 +68,6 @@ public class FrontendLoadMI implements LogicChunk, SubMenuChunk, DockFrontendLis { } - @Override - public String getName() - { - return "DockFrontend Configuration Loader"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - @Override public void setMenu(JMenu aMenu) { @@ -124,32 +130,28 @@ public class FrontendLoadMI implements LogicChunk, SubMenuChunk, DockFrontendLis } /** - * Utility method to keep the refMenu in sync with the available dock - * configurations + * Helper method that keeps various UI components synchronized. */ protected void updateGui() { - Set currSet; - JMenuItem tmpMI; - // Remove the old items refMenu.removeAll(); // Add all of the current configurations - currSet = refFrontend.getSettings(); - for (String aStr : currSet) + var currS = refFrontend.getSettings(); + for (String aStr : currS) { // Do not add hidden configurations if (aStr.charAt(0) != '.') { - tmpMI = new JMenuItem(aStr); + var tmpMI = new JMenuItem(aStr); tmpMI.addActionListener(this); refMenu.add(tmpMI); } } // Ensure we have items (to be enabled) - refMenu.setEnabled(currSet.size() > 0); + refMenu.setEnabled(currS.size() > 0); } } diff --git a/src/glum/logic/dock/FrontendManageMI.java b/src/glum/logic/dock/FrontendManageMI.java index 37e2a7e..946d896 100644 --- a/src/glum/logic/dock/FrontendManageMI.java +++ b/src/glum/logic/dock/FrontendManageMI.java @@ -1,27 +1,43 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.dock; import javax.swing.JFrame; - import bibliothek.gui.DockFrontend; - import glum.gui.dock.FrontendManageConfigPanel; import glum.logic.LogicChunk; import glum.registry.Registry; +/** + * {@link LogicChunk} used to display the various dockable configurations and allow the user to switch to a specific + * configuration.. + * + * @author lopeznr1 + */ public class FrontendManageMI implements LogicChunk { - private FrontendManageConfigPanel myPanel; - + // State vars + private final FrontendManageConfigPanel myPanel; + + /** Standard Constructor */ public FrontendManageMI(Registry aRegistry, String aLabel) { - JFrame aFrame; - DockFrontend aFrontend; - - aFrame = aRegistry.getSingleton("root.window", JFrame.class); - aFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); - - myPanel = new FrontendManageConfigPanel(aFrame, aFrontend); + var tmpFrame = aRegistry.getSingleton("root.window", JFrame.class); + var tmpFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); + + myPanel = new FrontendManageConfigPanel(tmpFrame, tmpFrontend); } @Override @@ -35,16 +51,4 @@ public class FrontendManageMI implements LogicChunk { } - @Override - public String getName() - { - return "DockFrontend Configuration Manager"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - } diff --git a/src/glum/logic/dock/FrontendSaveMI.java b/src/glum/logic/dock/FrontendSaveMI.java index c556d6d..3f3e4c9 100644 --- a/src/glum/logic/dock/FrontendSaveMI.java +++ b/src/glum/logic/dock/FrontendSaveMI.java @@ -1,26 +1,42 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.dock; import javax.swing.JFrame; +import bibliothek.gui.DockFrontend; import glum.gui.dock.FrontendAddConfigPanel; import glum.logic.LogicChunk; import glum.registry.Registry; -import bibliothek.gui.DockFrontend; - +/** + * {@link LogicChunk} used to allow an end user to save a dockable configuration. + * + * @author lopeznr1 + */ public class FrontendSaveMI implements LogicChunk { - protected FrontendAddConfigPanel myPanel; - + // State vars + private FrontendAddConfigPanel myPanel; + + /** Standard Constructor */ public FrontendSaveMI(Registry aRegistry, String aLabel) { - JFrame aFrame; - DockFrontend aFrontend; - - aFrame = aRegistry.getSingleton("root.window", JFrame.class); - aFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); + var tmpFrame = aRegistry.getSingleton("root.window", JFrame.class); + var tmpFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); - myPanel = new FrontendAddConfigPanel(aFrame, aFrontend); + myPanel = new FrontendAddConfigPanel(tmpFrame, tmpFrontend); } @Override @@ -34,16 +50,4 @@ public class FrontendSaveMI implements LogicChunk { } - @Override - public String getName() - { - return "DockFrontend Configuration Saver"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - } diff --git a/src/glum/logic/dock/FrontendShowMI.java b/src/glum/logic/dock/FrontendShowMI.java index 337e676..1334776 100644 --- a/src/glum/logic/dock/FrontendShowMI.java +++ b/src/glum/logic/dock/FrontendShowMI.java @@ -1,50 +1,65 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.dock; import javax.swing.JMenuItem; +import bibliothek.gui.DockFrontend; import glum.logic.LogicChunk; import glum.logic.MenuItemChunk; import glum.registry.Registry; -import bibliothek.gui.DockFrontend; -import bibliothek.gui.Dockable; - +/** + * {@link LogicChunk} used to allow an end user to display a dockable. + * + * @author lopeznr1 + */ public class FrontendShowMI implements LogicChunk, MenuItemChunk { + // Ref vars + private final DockFrontend refFrontend; + // State vars - protected DockFrontend refFrontend; - protected String labelStr; - protected String refName; - + private String labelStr; + private String refName; + + /** Standard Constructor */ public FrontendShowMI(Registry aRegistry, String aLabel) { - String[] tokens; - refFrontend = aRegistry.getSingleton(DockFrontend.class, DockFrontend.class); - - tokens = aLabel.split(":"); - if (tokens.length != 2) + + var tokenArr = aLabel.split(":"); + if (tokenArr.length != 2) throw new RuntimeException("Invalid label specification for LogicChunk.\n Label: " + aLabel); - - labelStr = tokens[0]; - refName = tokens[1]; + + labelStr = tokenArr[0]; + refName = tokenArr[1]; } @Override public void activate() { - Dockable aDockable; - - aDockable = refFrontend.getDockable(refName); - if (aDockable == null) + var tmpDockable = refFrontend.getDockable(refName); + if (tmpDockable == null) { System.out.println("Failed to locate a dockable with the name: " + refName); return; } - + // refFrontend.hide(aDockable); - refFrontend.show(aDockable); - aDockable.getDockParent().setFrontDockable(aDockable); + refFrontend.show(tmpDockable); + tmpDockable.getDockParent().setFrontDockable(tmpDockable); } @Override @@ -52,18 +67,6 @@ public class FrontendShowMI implements LogicChunk, MenuItemChunk { } - @Override - public String getName() - { - return "DockFrontend Dock Shower"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - @Override public void setMenuItem(JMenuItem aMI) { diff --git a/src/glum/logic/misc/ConsoleEchoMI.java b/src/glum/logic/misc/ConsoleEchoMI.java index 324c41f..a6a0812 100644 --- a/src/glum/logic/misc/ConsoleEchoMI.java +++ b/src/glum/logic/misc/ConsoleEchoMI.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.misc; import javax.swing.JMenuItem; @@ -7,27 +20,31 @@ import glum.logic.MenuItemChunk; import glum.registry.Registry; /** - * LogicChunk used to echo a message to the console. + * {@link LogicChunk} used to echo a message to the console. + * + * @author lopeznr1 */ public class ConsoleEchoMI implements LogicChunk, MenuItemChunk { - private String message; - private String title; + // Attributes + private final String message; + private final String title; + /** Standard Constructor */ public ConsoleEchoMI(Registry aRegistry, String aLabel) { - String[] strArr; - - title = aLabel; - message = "No message specified."; - // Customize the message and MenuItem title - strArr = aLabel.split(":"); + var strArr = aLabel.split(":"); if (strArr.length == 2) { title = strArr[0]; message = strArr[1]; } + else + { + title = aLabel; + message = "No message specified."; + } } @Override @@ -42,18 +59,6 @@ public class ConsoleEchoMI implements LogicChunk, MenuItemChunk ; // Nothing to do } - @Override - public String getName() - { - return "Console Echo MI"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - @Override public void setMenuItem(JMenuItem aMI) { diff --git a/src/glum/logic/misc/MemoryDialogMI.java b/src/glum/logic/misc/MemoryDialogMI.java index 15dde61..20dfdce 100644 --- a/src/glum/logic/misc/MemoryDialogMI.java +++ b/src/glum/logic/misc/MemoryDialogMI.java @@ -1,16 +1,38 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.logic.misc; -import glum.gui.dialog.MemoryUtilDialog; -import glum.logic.LogicChunk; -import glum.registry.Registry; - import javax.swing.JFrame; +import glum.gui.memory.MemoryUtilDialog; +import glum.logic.LogicChunk; +import glum.registry.Registry; + +/** + * {@link LogicChunk} used to show a UI component of the memory state of the application. + * + * @author lopeznr1 + */ public class MemoryDialogMI implements LogicChunk { - private JFrame refMainFrame; + // Ref vars + private final JFrame refMainFrame; + + // State vars private MemoryUtilDialog myDialog; - + + /** Standard Constructor */ public MemoryDialogMI(Registry aRegistry, String aLabel) { refMainFrame = aRegistry.getSingleton("root.window", JFrame.class); @@ -23,7 +45,7 @@ public class MemoryDialogMI implements LogicChunk // Lazy initialization if (myDialog == null) myDialog = new MemoryUtilDialog(refMainFrame); - + myDialog.setVisible(true); } @@ -32,16 +54,4 @@ public class MemoryDialogMI implements LogicChunk { } - @Override - public String getName() - { - return "Memory Util Dialog"; - } - - @Override - public String getVersion() - { - return "0.1"; - } - } diff --git a/src/glum/math/FastMath.java b/src/glum/math/FastMath.java deleted file mode 100644 index a4475fd..0000000 --- a/src/glum/math/FastMath.java +++ /dev/null @@ -1,614 +0,0 @@ -package glum.math; - -final public class FastMath -{ - - private FastMath(){} - - - /** A "close to zero" double epsilon value for use*/ - public static final double DBL_EPSILON = 2.220446049250313E-16d; - - - /** A "close to zero" double epsilon value for use*/ - public static final double FLT_EPSILON = 1.1920928955078125E-7d; - - - /** A "close to zero" double epsilon value for use*/ - public static final double ZERO_TOLERANCE = 0.0001d; - - public static final double ONE_THIRD = 1.0/3.0; - - - /** The value PI as a double. (180 degrees) */ - public static final double PI = Math.PI; - - - /** The value 2PI as a double. (360 degrees) */ - public static final double TWO_PI = 2.0 * PI; - - - /** The value PI/2 as a double. (90 degrees) */ - public static final double HALF_PI = 0.5 * PI; - - - /** The value PI/4 as a double. (45 degrees) */ - public static final double QUARTER_PI = 0.25 * PI; - - - /** The value 1/PI as a double. */ - public static final double INV_PI = 1.0 / PI; - - - /** The value 1/(2PI) as a double. */ - public static final double INV_TWO_PI = 1.0 / TWO_PI; - - - /** A value to multiply a degree value by, to convert it to radians. */ - public static final double DEG_TO_RAD = PI / 180.0; - - - /** A value to multiply a radian value by, to convert it to degrees. */ - public static final double RAD_TO_DEG = 180.0 / PI; - - - /** A precreated random object for random numbers. */ - //! public static final Random rand = new Random(System.currentTimeMillis()); - - - - - /** - * Returns true if the number is a power of 2 (2,4,8,16...) - * - * A good implementation found on the Java boards. note: a number is a power - * of two if and only if it is the smallest number with that number of - * significant bits. Therefore, if you subtract 1, you know that the new - * number will have fewer bits, so ANDing the original number with anything - * less than it will give 0. - * - * @param number - * The number to test. - * @return True if it is a power of two. - */ - public static boolean isPowerOfTwo(int number) { - return (number > 0) && (number & (number - 1)) == 0; - } - - public static int nearestPowerOfTwo(int number) { - return (int)Math.pow(2, Math.ceil(Math.log(number) / Math.log(2))); - } - - - /** - * Linear interpolation from startValue to endValue by the given percent. - * Basically: ((1 - percent) * startValue) + (percent * endValue) - * - * @param percent - * Percent value to use. - * @param startValue - * Begining value. 0% of f - * @param endValue - * ending value. 100% of f - * @return The interpolated value between startValue and endValue. - */ - public static double LERP(double percent, double startValue, double endValue) { - if (startValue == endValue) return startValue; - return ((1 - percent) * startValue) + (percent * endValue); - } - - - - - /** - * Returns the arc cosine of an angle given in radians.
    - * Special cases: - *

    • If fValue is smaller than -1, then the result is PI. - *
    • If the argument is greater than 1, then the result is 0.
    - * @param fValue The angle, in radians. - * @return fValue's acos - * @see java.lang.Math#acos(double) - */ - public static double acos(double fValue) { - if (-1.0f < fValue) { - if (fValue < 1.0f) - return Math.acos(fValue); - - return 0.0f; - } - - return PI; - } - - - /** - * Returns the arc sine of an angle given in radians.
    - * Special cases: - *
    • If fValue is smaller than -1, then the result is -HALF_PI. - *
    • If the argument is greater than 1, then the result is HALF_PI.
    - * @param fValue The angle, in radians. - * @return fValue's asin - * @see java.lang.Math#asin(double) - */ - public static double asin(double fValue) { - if (-1.0f < fValue) { - if (fValue < 1.0f) - return Math.asin(fValue); - - return HALF_PI; - } - - return -HALF_PI; - } - - - /** - * Returns the arc tangent of an angle given in radians.
    - * @param fValue The angle, in radians. - * @return fValue's asin - * @see java.lang.Math#atan(double) - */ - public static double atan(double fValue) { - return Math.atan(fValue); - } - - - /** - * A direct call to Math.atan2. - * @param fY - * @param fX - * @return Math.atan2(fY,fX) - * @see java.lang.Math#atan2(double, double) - */ - public static double atan2(double fY, double fX) { - return Math.atan2(fY, fX); - } - - - /** - * Rounds a fValue up. A call to Math.ceil - * @param fValue The value. - * @return The fValue rounded up - * @see java.lang.Math#ceil(double) - */ - public static double ceil(double fValue) { - return Math.ceil(fValue); - } - - /** - * Fast Trig functions for x86. This forces the trig functiosn to stay - * within the safe area on the x86 processor (-45 degrees to +45 degrees) - * The results may be very slightly off from what the Math and StrictMath - * trig functions give due to rounding in the angle reduction but it will be - * very very close. - * - * note: code from wiki posting on java.net by jeffpk - */ - public static double reduceSinAngle(double radians) { - radians %= TWO_PI; // put us in -2PI to +2PI space - if (Math.abs(radians) > PI) { // put us in -PI to +PI space - radians = radians - (TWO_PI); - } - if (Math.abs(radians) > HALF_PI) {// put us in -PI/2 to +PI/2 space - radians = PI - radians; - } - - - return radians; - } - - - /** - * Returns sine of a value. - * - * note: code from wiki posting on java.net by jeffpk - * - * @param fValue - * The value to sine, in radians. - * @return The sine of fValue. - * @see java.lang.Math#sin(double) - */ -/*! public static double sin(double fValue) { - fValue = reduceSinAngle(fValue); // limits angle to between -PI/2 and +PI/2 - if (Math.abs(fValue)<=Math.PI/4){ - return Math.sin(fValue); - } - - return Math.cos(Math.PI/2-fValue); - } -*/ - - /** - * Returns cos of a value. - * - * @param fValue - * The value to cosine, in radians. - * @return The cosine of fValue. - * @see java.lang.Math#cos(double) - */ -/*! public static double cos(double fValue) { - return sin(fValue+HALF_PI); - } -*/ - - /** - * Returns E^fValue - * @param fValue Value to raise to a power. - * @return The value E^fValue - * @see java.lang.Math#exp(double) - */ -/*! public static double exp(double fValue) { - return Math.exp(fValue); - } -*/ - - /** - * Returns Absolute value of a double. - * @param fValue The value to abs. - * @return The abs of the value. - * @see java.lang.Math#abs(double) - */ - public static double abs(double fValue) { - if (fValue < 0) return -fValue; - return fValue; - } - - - /** - * Returns a number rounded down. - * @param fValue The value to round - * @return The given number rounded down - * @see java.lang.Math#floor(double) - */ - public static double floor(double fValue) { - return Math.floor(fValue); - } - - - /** - * Returns 1/sqrt(fValue) - * @param fValue The value to process. - * @return 1/sqrt(fValue) - * @see java.lang.Math#sqrt(double) - */ - public static double invSqrt(double fValue) { - return (1.0f / Math.sqrt(fValue)); - } - - - /** - * Returns the log base E of a value. - * @param fValue The value to log. - * @return The log of fValue base E - * @see java.lang.Math#log(double) - */ - public static double log(double fValue) { - return Math.log(fValue); - } - - /** - * Returns the logarithm of value with given base, calculated as log(value)/log(base), - * so that pow(base, return)==value (contributed by vear) - * @param value The value to log. - * @param base Base of logarithm. - * @return The logarithm of value with given base - */ - public static double log(double value, double base) { - return (Math.log(value)/Math.log(base)); - } - - - /** - * Returns a number raised to an exponent power. fBase^fExponent - * @param fBase The base value (IE 2) - * @param fExponent The exponent value (IE 3) - * @return base raised to exponent (IE 8) - * @see java.lang.Math#pow(double, double) - */ -/*! public static double pow(double fBase, double fExponent) { - return Math.pow(fBase, fExponent); - } -*/ - - /** - * Returns the value squared. fValue ^ 2 - * @param fValue The vaule to square. - * @return The square of the given value. - */ - public static double sqr(double fValue) { - return fValue * fValue; - } - - - /** - * Returns the square root of a given value. - * @param fValue The value to sqrt. - * @return The square root of the given value. - * @see java.lang.Math#sqrt(double) - */ - public static double sqrt(double fValue) { - return Math.sqrt(fValue); - } - - - /** - * Returns the tangent of a value. If USE_FAST_TRIG is enabled, an approximate value - * is returned. Otherwise, a direct value is used. - * @param fValue The value to tangent, in radians. - * @return The tangent of fValue. - * @see java.lang.Math#tan(double) - */ - public static double tan(double fValue) { - return Math.tan(fValue); - } - - - - - - - - /** - * Returns the integral value of a given value. - * @param fValue The value to round. - * @return The square root of the given value. - * @see java.lang.Math#round(double) - */ - public static double round(double fValue) { - return Math.round(fValue); - } - - -//compute sine -/*public static double sin(double x) -{ - double aAns; - - if (x < -3.14159265) - x += 6.28318531; - else if (x > 3.14159265) - x -= 6.28318531; - - if (x < 0) - { - aAns = 1.27323954 * x + .405284735 * x * x; - - if (aAns < 0) - aAns = .225 * (aAns *-aAns - aAns) + aAns; - else - aAns = .225 * (aAns * aAns - aAns) + aAns; - } - else - { - aAns = 1.27323954 * x - 0.405284735 * x * x; - - if (aAns < 0) - aAns = .225 * (aAns *-aAns - aAns) + aAns; - else - aAns = .225 * (aAns * aAns - aAns) + aAns; - } - - return aAns; -} - - -public static double cos(double x) -{ - return sin(x + 1.57079632); -} -*/ - -//compute cosine: sin(x + PI/2) = cos(x) -/*public static double cos(double x) -{ - double aAns; - - x += 1.57079632; - if (x > 3.14159265) - x -= 6.28318531; - - if (x < 0) - { - aAns = 1.27323954 * x + 0.405284735 * x * x; - - if (aAns < 0) - aAns = .225 * (aAns *-aAns - aAns) + aAns; - else - aAns = .225 * (aAns * aAns - aAns) + aAns; - } - else - { - aAns = 1.27323954 * x - 0.405284735 * x * x; - - if (aAns < 0) - aAns = .225 * (aAns *-aAns - aAns) + aAns; - else - aAns = .225 * (aAns * aAns - aAns) + aAns; - } - - return aAns; -} -*/ - - - - -public static final double invFact2 = 1.0/2.0; -public static final double invFact3 = 1.0/6.0; -public static final double invFact4 = 1.0/24.0; -public static final double invFact5 = 1.0/120.0; -public static final double invFact6 = 1.0/720.0; -public static final double invFact7 = 1.0/5040.0; -public static final double invFact8 = 1.0/40320.0; -public static final double invFact9 = 1.0/362880.0; -public static final double invFact11 = 1.0/39916800.0; -public static final double invFact13 = 1.0/6227020800.0; -public static final double invFact15 = 1.0/1307674368000.0; - -public static final double invFact2n = -1.0/2.0; -public static final double invFact3n = -1.0/6.0; -public static final double invFact4n = -1.0/24.0; -public static final double invFact5n = -1.0/120.0; -public static final double invFact6n = -1.0/720.0; -public static final double invFact7n = -1.0/5040.0; -public static final double invFact8n = -1.0/40320.0; -public static final double invFact9n = -1.0/362880.0; -public static final double invFact11n = -1.0/39916800.0; - - -/*public static double cos(double x) -{ - double x2, x4, x6; - - while (x < -Math.PI) - x += TWO_PI; - - while (x > Math.PI) - x -= TWO_PI; - - x2 = x * x; - x4 = x2 * x2; - x6 = x4 * x2; - - return 1 - invFact2*x2 + invFact4*x4 - invFact6*x6; -}*/ -public static double cos(double x) -{ - return sin(x + HALF_PI); -} - - -public static double sin(double x) -{ -// double x2, x3, x5, x7; -// double x2, x3, x4; - double x2, x3; - - while (x < -Math.PI) - x += TWO_PI; - - while (x > Math.PI) - x -= TWO_PI; - -// x2 = x * x; -// x3 = x2 * x; -// x5 = x3 * x2; -// x7 = x5 * x2; -// return x - invFact3*x3 + invFact5*x5 - invFact7*x7; - - -// x2 = x * x; -// x3 = x2 * x; -// x4 = x3 * x; -// return x + x3*(-invFact3 + invFact5*x2 - invFact7*x4) - -// x2 = x * x; -// x3 = x2 * x; -// return x + x3*(-invFact3 + x2*(invFact5 - invFact7*x2)); - -// x2 = x * x; -// x3 = x2 * x; -// return x + x3*(-invFact3 + x2*(invFact5 + x2*(-invFact7 + x2*(invFact9 - x2*invFact11)))); - - x2 = x * x; - x3 = x2 * x; - return x + x3*(-invFact3 + x2*(invFact5 + x2*(-invFact7 - + x2*(invFact9 + x2*(-invFact11 + x2*invFact13))))); -} -//public static double sin(double x) -//{ -// return cos(x - 1.57079632679); //! This does not work -//} - - - - -/*//compute sine -public static double sin(double x) -{ - double aAns; - - if (x < -3.14159265) - x += 6.28318531; - else if (x > 3.14159265) - x -= 6.28318531; - - if (x < 0) - aAns = 1.27323954 * x + 0.405284735 * x * x; - else - aAns = 1.27323954 * x - 0.405284735 * x * x; - - return aAns; -} - - -//compute cosine: sin(x + PI/2) = cos(x) -public static double cos(double x) -{ - double aAns; - - x += 1.57079632; - if (x > 3.14159265) - x -= 6.28318531; - - if (x < 0) - aAns = 1.27323954 * x + 0.405284735 * x * x; - else - aAns = 1.27323954 * x - 0.405284735 * x * x; - - return aAns; -} -*/ - - - - - - - - - - - - - - - - /** - * pow - Method to replace Math.pow(). - * THis method should be faster but results in a coarser solution. - * Grabed from: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/ - */ - public static double pow(final double a, final double b) - { - final int x = (int) (Double.doubleToLongBits(a) >> 32); - final int y = (int) (b * (x - 1072632447) + 1072632447); - return Double.longBitsToDouble(((long) y) << 32); - } - - - - /** - * exp - Method to replace Math.exp(). - * THis method should be faster but results in a coarser solution. - * Grabed from: http://martin.ankerl.com/2007/10/04/optimized-pow-approximation-for-java-and-c-c/ - */ - public static double exp(double val) - { - final long tmp = (long) (1512775 * val + (1072693248 - 60801)); - return Double.longBitsToDouble(tmp << 32); - } - - - - public static double min(double aVal, double bVal) - { -// return Math.min(aVal, bVal); - if (aVal < bVal) - return aVal; - - return bVal; - } - - -} \ No newline at end of file diff --git a/src/glum/math/FractionTransformer.java b/src/glum/math/FractionTransformer.java deleted file mode 100644 index 1d2db02..0000000 --- a/src/glum/math/FractionTransformer.java +++ /dev/null @@ -1,93 +0,0 @@ -package glum.math; - -public enum FractionTransformer -{ - Linear("Linear", "LINEAR") - { - @Override - public double transform(double aFrac) - { - return aFrac; - } - }, - - Cosine("Cosine", "COSINE") - { - @Override - public double transform(double aFrac) - { - aFrac = aFrac * Math.PI; - aFrac = (1 - Math.cos(aFrac)) * 0.5; - - return aFrac; - } - }, - - Cubic("Cubic", "CUBIC") - { - @Override - public double transform(double aFrac) - { - double t, t2; - - t = aFrac; - t2 = aFrac * aFrac; - aFrac = 3*t2 - 2*t2*t; - - return aFrac; - } - }; - - - // Vars - private final String userStr; - private final String encodeStr; - - - /** - * Constructor - */ - FractionTransformer(String aUserStr, String aEncodeStr) - { - userStr = aUserStr; - encodeStr = aEncodeStr; - } - - - /** - * getReferenceName - */ - public String getEncodeString() - { - return encodeStr; - } - - /** - * transform - */ - public abstract double transform(double aFrac); - - /** - * parse - Returns the associated enum type - */ - public static FractionTransformer parse(String aStr) - { - if (aStr == null) - return null; - - for (FractionTransformer aEnum : FractionTransformer.values()) - { - if (aStr.equals(aEnum.getEncodeString()) == true) - return aEnum; - } - - return null; - } - - @Override - public String toString() - { - return userStr; - } - -} diff --git a/src/glum/math/GradientMesh.java b/src/glum/math/GradientMesh.java deleted file mode 100644 index 561c1fc..0000000 --- a/src/glum/math/GradientMesh.java +++ /dev/null @@ -1,170 +0,0 @@ -package glum.math; - -import static java.lang.Math.*; -import java.util.*; - -import glum.coord.*; - -public class GradientMesh -{ - // Level of detail - int lod; - int bitMask; - - // Reference gradients - double[] grad1D; - Point2D[] grad2D; - Point3D[] grad3D; - - /** - * Constructor - */ - public GradientMesh(long aSeed, int aLod) - { - Random aRand; - - // lod: Level of detail. The number of reference gradients for - // each dimension is equal to 2^lod. Thus we support [64, 1048576] - // reference gradients. Erroneous input will be silently clamped. - lod = aLod; - if (lod < 6) - lod = 6; - else if (lod > 20) - lod = 20; - - // Compute the bit mask associated with lod - bitMask = 0; - for (int c1 = 0; c1 < lod; c1++) - bitMask = (bitMask << 1) + 1; - - // Construct our reference items - aRand = new Random(aSeed); - constructMesh1D(aRand); - constructMesh2D(aRand); -//aRand = new Random(3432); - constructMesh3D(aRand); - } - - public double getGrad1D(int randVal) - { - return grad1D[randVal & bitMask]; - } - - public Point2D getGrad2D(int randVal) - { - return grad2D[randVal & bitMask]; - } - - public Point3D getGrad3D(int randVal) - { - return grad3D[randVal & bitMask]; - } - - /** - * constructMesh1D - */ - protected void constructMesh1D(Random aRand) - { - int numItems; - - numItems = 1 << lod; - grad1D = new double[numItems]; - for (int c1 = 0; c1 < numItems; c1++) - { - if (aRand.nextDouble() < 0.5) - grad1D[c1] = -1; - else - grad1D[c1] = 1; - } - } - - /** - * constructMesh2D - */ - protected void constructMesh2D(Random aRand) - { - Point2D aPt; - double theta; - int numItems; - - numItems = 1 << lod; - grad2D = new Point2D[numItems]; - for (int c1 = 0; c1 < numItems; c1++) - { - // Compute a random point on the unit circle. - aPt = new Point2D(); - - theta = 2 * PI * aRand.nextDouble(); - aPt.x = cos(theta); - aPt.y = sin(theta); - grad2D[c1] = aPt; -/* - while (true) - { - aPt.x = 1.0 - aRand.nextDouble()*2; - aPt.z = 1.0 - aRand.nextDouble()*2; - - if (aPt.x * aPt.x + aPt.y * aPt.y <= 1.0) - break; - } - - len = sqrt((aPt.x * aPt.x) + (aPt.y * aPt.y)); - aPt.x = aPt.x / len; - aPt.y = aPt.y / len; - grad3D[c1] = aPt; -*/ - } - } - - /** - * constructMesh3D - */ - protected void constructMesh3D(Random aRand) - { - Point3D aPt; - double u, v, theta, phi; - int numItems; - - numItems = 1 << lod; - grad3D = new Point3D[numItems]; - for (int c1 = 0; c1 < numItems; c1++) - { - // Compute a random point on a sphere. The logic is taken from the site: - // http://mathworld.wolfram.com/SpherePointPicking.html - // Note you can not pick 3 points x,y, and z and then normalize them to - // the unit vector because these points are constrained by the equation: - // x^2 + y^2 + z^2 = 1 - aPt = new Point3D(); - - u = aRand.nextDouble(); - v = aRand.nextDouble(); - - theta = 2 * PI * u; - phi = acos(2*v - 1); - - // Go from phi, theta to x,y,z on the unit sphere - aPt.x = 1.0*cos(theta)*sin(phi); - aPt.y = 1.0*sin(theta)*sin(phi); - aPt.z = 1.0*cos(phi); - grad3D[c1] = aPt; -/* - while (true) - { - aPt.x = 1.0 - aRand.nextDouble()*2; - aPt.y = 1.0 - aRand.nextDouble()*2; - aPt.z = 1.0 - aRand.nextDouble()*2; - - if (aPt.x * aPt.x + aPt.y * aPt.y + aPt.z * aPt.z <= 1.0) - break; - } - - len = sqrt((aPt.x * aPt.x) + (aPt.y * aPt.y) + (aPt.z * aPt.z)); - aPt.x = aPt.x / len; - aPt.y = aPt.y / len; - aPt.z = aPt.z / len; - grad3D[c1] = aPt; -*/ - } - } - -} diff --git a/src/glum/math/GroupNoise.java b/src/glum/math/GroupNoise.java deleted file mode 100644 index a975a20..0000000 --- a/src/glum/math/GroupNoise.java +++ /dev/null @@ -1,94 +0,0 @@ -package glum.math; - -import java.util.*; - -public class GroupNoise implements Noise -{ - // Collection of individual noise objects - protected Collection myNoiseList; - - /** - * Constructor - */ - public GroupNoise() - { - myNoiseList = new LinkedList(); - } - - public GroupNoise(Collection aNoiseList) - { - myNoiseList = new LinkedList(aNoiseList); - } - - /** - * Adds a Noise object to this GroupNoise - */ - public void add(Noise aNoise) - { - myNoiseList.add(aNoise); - } - - @Override - public double getAmplitude() - { - double maxAmp, aAmp; - - maxAmp = 0; - for (Noise aNoise : myNoiseList) - { - aAmp = aNoise.getAmplitude(); - if (aAmp > maxAmp) - maxAmp = aAmp; - } - - return maxAmp; - } - - /** - * getNoiseList - Returns the list of individual noises - */ - public Collection getNoiseList() - { - List aNoiseList; - - aNoiseList = new LinkedList(myNoiseList); - return aNoiseList; - } - - @Override - public double getValue1D(double x) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue1D(x); - - return total; - } - - @Override - public double getValue2D(double x, double y) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue2D(x, y); - - return total; - } - - @Override - public double getValue3D(double x, double y, double z) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue3D(x, y, z); - - return total; - } - -} diff --git a/src/glum/math/Noise.java b/src/glum/math/Noise.java deleted file mode 100644 index 11ecedd..0000000 --- a/src/glum/math/Noise.java +++ /dev/null @@ -1,28 +0,0 @@ -package glum.math; - -public interface Noise -{ - /** - * getAmplitude - Returns the maximum offset produced by this noise - */ - public double getAmplitude(); - - /** - * getValue1D - Returns a continous random value in the range of [0-1] in 1D space. It is repeatable for the same x - * values. - */ - public double getValue1D(double x); - - /** - * getValue2D - Returns a continous random value in the range of [0-1] in 2D space. It is repeatable for the same x,y - * values. - */ - public double getValue2D(double x, double y); - - /** - * getValue3D - Returns a continous random value in the range of [0-1] in 3D space. It is repeatable for the same - * x,y,z values. - */ - public double getValue3D(double x, double y, double z); - -} diff --git a/src/glum/math/PerlinNoise.java b/src/glum/math/PerlinNoise.java deleted file mode 100644 index eff99f0..0000000 --- a/src/glum/math/PerlinNoise.java +++ /dev/null @@ -1,367 +0,0 @@ -package glum.math; - -import static java.lang.Math.*; -import java.util.*; - -import glum.coord.*; - -public class PerlinNoise implements Noise -{ - // Seed key values - protected GradientMesh myGradientMesh; - protected FractionTransformer myFracTransformer; - protected double amp, freq; - - protected int[] p0Table, p1Table, p2Table, p3Table; - protected int[] p4Table, p5Table, p6Table, p7Table; - protected int[] p8Table, p9Table, p10Table, p11Table; - - /** - * Constructor - */ - public PerlinNoise(long aSeed) - { - this(aSeed, null); - } - - public PerlinNoise(long aSeed, GradientMesh aGradientMesh) - { - Vector aList; - Random aRandom; - - // Set up our rand generater - aRandom = new Random(aSeed); - - // Set up the fraction tranform and function - myFracTransformer = FractionTransformer.Cubic; - - // If no GradientMesh specified then construct our own reference gradient - // mesh with 2^12 reference points. - myGradientMesh = aGradientMesh; - if (myGradientMesh == null) - myGradientMesh = new GradientMesh(aRandom.nextLong(), 12); - - // Construct the words to use in the permutation - aList = new Vector(); - for (int c1 = 0; c1 < 256; c1++) - aList.add(c1); - - Collections.shuffle(aList, aRandom); - p0Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p0Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p1Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p1Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p2Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p2Table[c1] = aList.get(c1) << 16; - - Collections.shuffle(aList, aRandom); - p4Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p4Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p5Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p5Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p6Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p6Table[c1] = aList.get(c1) << 16; - - Collections.shuffle(aList, aRandom); - p8Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p8Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p9Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p9Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p10Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p10Table[c1] = aList.get(c1) << 16; - -/* -int x = 0xF0F0F0F0; -System.out.print(" x[0]:" + ((x & 0x000000ff) >> 0) ); -System.out.print(" x[1]:" + ((x & 0x0000ff00) >> 8) ); -System.out.print(" x[2]:" + ((x & 0x00ff0000) >> 16) ); -System.out.println(" x[3]:" + ((x & 0xff000000) >> 24) ); - -System.out.println("Hmm..."); -System.out.print(" x[0]:" + ((x >> 0) & 0xff) ); -System.out.print(" x[1]:" + ((x >> 8) & 0xff) ); -System.out.print(" x[2]:" + ((x >> 16) & 0xff) ); -System.out.println(" x[3]:" + ((x >> 24) & 0xff) ); -System.exit(0); -*/ - } - - - /** - * setPersistence - Set the amplitude and frequency associated - * with the noise function. - */ - public void setPersistence(double aAmp, double aFreq) - { - amp = aAmp; - freq = aFreq; - } - - - @Override - public double getAmplitude() - { - return amp; - } - - - - - @Override - public double getValue1D(double x) - { - return getInterpolatedValue1D(x * freq) * amp; - } - - - public double getInterpolatedValue1D(double x) - { - int iX; - double fracX; - double left, right; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - left = getSmoothValue1D(x, iX); - right = getSmoothValue1D(x, iX + 1); - - return interpolate(left, right, fracX); - } - - - public double getSmoothValue1D(double x, int qX) - { - int vx; -// int w1, w2, w3, w4; - int w1, w2, w3; - double gradPt; - double value; - - vx = qX; - w1 = (vx >> 0) & 0xFF; - w2 = (vx >> 8) & 0xFF; - w3 = (vx >> 16) & 0xFF; -// w4 = (vx >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); - - // Return a value from -1 to 1 - gradPt = myGradientMesh.getGrad1D(vx); - value = gradPt * (x - qX); - return value; - } - - - - - - - - - - - @Override - public double getValue2D(double x, double y) - { - return getInterpolatedValue2D(x * freq, y * freq) * amp; - } - - - public double getInterpolatedValue2D(double x, double y) - { - int iX, iY; - double fracX, fracY; - double tL, tR, bL, bR, topRow, botRow; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - iY = (int)floor(y); - fracY = y - iY; - fracY = myFracTransformer.transform(fracY); - - tL = getSmoothValue2D(x, y, iX, iY); - tR = getSmoothValue2D(x, y, iX + 1, iY); - bL = getSmoothValue2D(x, y, iX, iY + 1); - bR = getSmoothValue2D(x, y, iX + 1, iY + 1); - - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - - return interpolate(topRow, botRow, fracY); - } - - - public double getSmoothValue2D(double x, double y, int qX, int qY) - { - int vx, vy; -// int w1, w2, w3, w4; - int w1, w2, w3; - Point2D gradPt; - double value; - - vx = qX; - w1 = (vx >> 0) & 0xFF; - w2 = (vx >> 8) & 0xFF; - w3 = (vx >> 16) & 0xFF; -// w4 = (vx >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); - - vy = vx + qY; - w1 = (vy >> 0) & 0xFF; - w2 = (vy >> 8) & 0xFF; - w3 = (vy >> 16) & 0xFF; -// w4 = (vy >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vy = (p4Table[w1 & 0xff] ) | (p5Table[w2 & 0xff] ) | (p6Table[w3 & 0xff] ); - - // Return a value from -1 to 1 - gradPt = myGradientMesh.getGrad2D(vy); - value = (gradPt.x * (x - qX)) + (gradPt.y * (y - qY)); - return value; - } - - - - - - - - - - - - - - @Override - public double getValue3D(double x, double y, double z) - { - return getInterpolatedValue3D(x * freq, y * freq, z * freq) * amp; - } - - - public double getInterpolatedValue3D(double x, double y, double z) - { - int iX, iY, iZ; - double fracX, fracY, fracZ; - double tL, tR, bL, bR, topRow, botRow; - double planeZ1, planeZ2; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - iY = (int)floor(y); - fracY = y - iY; - fracY = myFracTransformer.transform(fracY); - - iZ = (int)floor(z); - fracZ = z - iZ; - fracZ = myFracTransformer.transform(fracZ); - - tL = getSmoothValue3D(x, y, z, iX, iY, iZ); - tR = getSmoothValue3D(x, y, z, iX + 1, iY, iZ); - bL = getSmoothValue3D(x, y, z, iX, iY + 1, iZ); - bR = getSmoothValue3D(x, y, z, iX + 1, iY + 1, iZ); - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - planeZ1 = interpolate(topRow, botRow, fracY); - - tL = getSmoothValue3D(x, y, z, iX, iY, iZ + 1); - tR = getSmoothValue3D(x, y, z, iX + 1, iY, iZ + 1); - bL = getSmoothValue3D(x, y, z, iX, iY + 1, iZ + 1); - bR = getSmoothValue3D(x, y, z, iX + 1, iY + 1, iZ + 1); - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - planeZ2 = interpolate(topRow, botRow, fracY); - - return interpolate(planeZ1, planeZ2, fracZ); - } - - - public double getSmoothValue3D(double x, double y, double z, int qX, int qY, int qZ) - { - int vx, vy, vz; -// int w1, w2, w3, w4; - int w1, w2, w3; - Point3D gradPt; - double value; - - vx = qX; - w1 = (vx >> 0) & 0xFF; - w2 = (vx >> 8) & 0xFF; - w3 = (vx >> 16) & 0xFF; -// w4 = (vx >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); - - vy = vx + qY; - w1 = (vy >> 0) & 0xFF; - w2 = (vy >> 8) & 0xFF; - w3 = (vy >> 16) & 0xFF; -// w4 = (vy >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vy = (p4Table[w1 & 0xff] ) | (p5Table[w2 & 0xff] ) | (p6Table[w3 & 0xff] ); - - vz = vy + qZ; - w1 = (vz >> 0) & 0xFF; - w2 = (vz >> 8) & 0xFF; - w3 = (vz >> 16) & 0xFF; -// w4 = (vz >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vz = (p8Table[w1 & 0xff] ) | (p9Table[w2 & 0xff] ) | (p10Table[w3 & 0xff] ); - - // Return a value from -1 to 1 - gradPt = myGradientMesh.getGrad3D(vz); - value = (gradPt.x * (x - qX)) + (gradPt.y * (y - qY)) + (gradPt.z * (z - qZ)); - return value; - } - - - /** - * interpolate - Returns a value between v1 and v2 wrt to frac. - */ - protected double interpolate(double v1, double v2, double frac) - { - return v1 + frac*(v2 - v1); - } - -} - diff --git a/src/glum/math/TransformNoise.java b/src/glum/math/TransformNoise.java deleted file mode 100644 index 2c840fb..0000000 --- a/src/glum/math/TransformNoise.java +++ /dev/null @@ -1,44 +0,0 @@ -package glum.math; - -public class TransformNoise implements Noise -{ - // State vars - protected Noise refNoise; - protected double offsetVal; - protected double scalarVal; - - /** - * Constructor - */ - public TransformNoise(Noise aRefNoise, double aOffsetVal, double aScalarVal) - { - refNoise = aRefNoise; - offsetVal = aOffsetVal; - scalarVal = aScalarVal; - } - - @Override - public double getAmplitude() - { - return scalarVal * refNoise.getAmplitude(); - } - - @Override - public double getValue1D(double x) - { - return offsetVal + (scalarVal * refNoise.getValue1D(x)); - } - - @Override - public double getValue2D(double x, double y) - { - return offsetVal + (scalarVal * refNoise.getValue2D(x, y)); - } - - @Override - public double getValue3D(double x, double y, double z) - { - return offsetVal + (scalarVal * refNoise.getValue3D(x, y, z)); - } - -} diff --git a/src/glum/math/TurbulentNoise.java b/src/glum/math/TurbulentNoise.java deleted file mode 100644 index 9f21a38..0000000 --- a/src/glum/math/TurbulentNoise.java +++ /dev/null @@ -1,40 +0,0 @@ -package glum.math; - -public class TurbulentNoise implements Noise -{ - // State vars - protected Noise refNoise; - - /** - * Constructor - */ - public TurbulentNoise(Noise aRefNoise) - { - refNoise = aRefNoise; - } - - @Override - public double getAmplitude() - { - return refNoise.getAmplitude(); - } - - @Override - public double getValue1D(double x) - { - return Math.abs(refNoise.getValue1D(x)); - } - - @Override - public double getValue2D(double x, double y) - { - return Math.abs(refNoise.getValue2D(x, y)); - } - - @Override - public double getValue3D(double x, double y, double z) - { - return Math.abs(refNoise.getValue3D(x, y, z)); - } - -} diff --git a/src/glum/math/ValueNoise.java b/src/glum/math/ValueNoise.java deleted file mode 100644 index 7f1f64f..0000000 --- a/src/glum/math/ValueNoise.java +++ /dev/null @@ -1,422 +0,0 @@ -package glum.math; - -import static java.lang.Math.*; -import java.util.*; - - -public class ValueNoise implements Noise -{ - // Seed key values - protected FractionTransformer myFracTransformer; - protected double amp, freq; - protected int p0, p1, p2, p3, p4; - protected int[] p0Table, p1Table, p2Table; - protected int[] p4Table, p5Table, p6Table, p7Table; - protected int[] p8Table, p9Table, p10Table, p11Table; - - /** - * Constructor - */ - public ValueNoise(long aSeed) - { - p0 = 57; - p1 = 15731; - p2 = 789221; - p3 = 1376312589; - - Vector aList; - Random aRandom; - - // Set up our rand generater - aRandom = new Random(aSeed); - - // Set up the fraction tranform function - myFracTransformer = FractionTransformer.Cubic; - - // Construct the words to use in the permutation - aList = new Vector(); - for (int c1 = 0; c1 < 256; c1++) - aList.add(c1); - - Collections.shuffle(aList, aRandom); - p0Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p0Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p1Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p1Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p2Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p2Table[c1] = aList.get(c1) << 16; - - Collections.shuffle(aList, aRandom); - p4Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p4Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p5Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p5Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p6Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p6Table[c1] = aList.get(c1) << 16; - - Collections.shuffle(aList, aRandom); - p8Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p8Table[c1] = aList.get(c1) << 0; - - Collections.shuffle(aList, aRandom); - p9Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p9Table[c1] = aList.get(c1) << 8; - - Collections.shuffle(aList, aRandom); - p10Table = new int[256]; - for (int c1 = 0; c1 < 256; c1++) - p10Table[c1] = aList.get(c1) << 16; - -/* -int x = 0xF0F0F0F0; -System.out.print(" x[0]:" + ((x & 0x000000ff) >> 0) ); -System.out.print(" x[1]:" + ((x & 0x0000ff00) >> 8) ); -System.out.print(" x[2]:" + ((x & 0x00ff0000) >> 16) ); -System.out.println(" x[3]:" + ((x & 0xff000000) >> 24) ); - -System.out.println("Hmm..."); -System.out.print(" x[0]:" + ((x >> 0) & 0xff) ); -System.out.print(" x[1]:" + ((x >> 8) & 0xff) ); -System.out.print(" x[2]:" + ((x >> 16) & 0xff) ); -System.out.println(" x[3]:" + ((x >> 24) & 0xff) ); -System.exit(0); -*/ - } - - public ValueNoise(int aP0, int aP1, int aP2, int aP3) - { - this(0); - - amp = 1.0; - freq = 1.0; - p0 = aP0; - p1 = aP1; - p2 = aP2; - p3 = aP3; - } - - - /** - * setPersistence - Set the amplitude and frequency associated - * with the noise function. - */ - public void setPersistence(double aAmp, double aFreq) - { - amp = aAmp; - freq = aFreq; - } - - - @Override - public double getAmplitude() - { - return amp; - } - - - @Override - public double getValue1D(double x) - { - return getInterpolatedValue1D(x * freq) * amp; - } - - - public double getInterpolatedValue1D(double x) - { - int iX; - double fracX; - double left, right; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - left = getSmoothValue1D(iX); - right = getSmoothValue1D(iX + 1); - - return interpolate(left, right, fracX); - } - - - public double getSmoothValue1D(int x) - { - double corners, center; - - corners = (getRawValue1D(x-1) + getRawValue1D(x+1)) / 2; - center = getRawValue1D(x) / 2; - return corners + center; - } - - - public double getRawValue1D(int x) - { - int vx; -// int w1, w2, w3, w4; - int w1, w2, w3; - -/* long aVal; - - aVal = x + (y * p0); - aVal = (aVal <<13) ^ aVal; - - return (1.0 - ((aVal * (aVal * aVal * p1 + p2) + p3) & 0x7fffffff) / 1073741824.0); -*/ - - vx = x; - w1 = (vx >> 0) & 0xFF; - w2 = (vx >> 8) & 0xFF; - w3 = (vx >> 16) & 0xFF; -// w4 = (vx >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); - - // Return a value from -1 to 1. The value of vy is in the range: [0, 2^24 - 1] - return 1.0 - (vx/8388607.5); - } - - - - - - - - - - - @Override - public double getValue2D(double x, double y) - { - return getInterpolatedValue2D(x * freq, y * freq) * amp; - } - - - public double getInterpolatedValue2D(double x, double y) - { - int iX, iY; - double fracX, fracY; - double tL, tR, bL, bR, topRow, botRow; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - iY = (int)floor(y); - fracY = y - iY; - fracY = myFracTransformer.transform(fracY); - - tL = getSmoothValue2D(iX, iY); - tR = getSmoothValue2D(iX + 1, iY); - bL = getSmoothValue2D(iX, iY + 1); - bR = getSmoothValue2D(iX + 1, iY + 1); - - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - - return interpolate(topRow, botRow, fracY); - } - - - public double getSmoothValue2D(int x, int y) - { -return getRawValue2D(x, y); -/* double corners, sides, center; - - corners = (getRawValue2D(x-1, y-1) + getRawValue2D(x+1, y-1) + getRawValue2D(x-1, y+1) + getRawValue2D(x+1, y+1)) / 16; - sides = (getRawValue2D(x-1, y) + getRawValue2D(x+1, y) + getRawValue2D(x, y-1) + getRawValue2D(x, y+1)) / 8; - center = getRawValue2D(x, y) / 4; - - return corners + sides + center; -*/ } - - - public double getRawValue2D(int x, int y) - { -// int vx, vy; -// int w1, w2, w3, w4; - - long aVal; - aVal = x + (y * p0); - aVal = (aVal <<13) ^ aVal; - - return (1.0 - ((aVal * (aVal * aVal * p1 + p2) + p3) & 0x7fffffff) / 1073741824.0); - - -// vx = x; -// w1 = (vx >> 0) & 0xFF; -// w2 = (vx >> 8) & 0xFF; -// w3 = (vx >> 16) & 0xFF; -//// w4 = (vx >> 24) & 0xFF; -// w2 = w2 + w1; -// w3 = w3 + w2; -//// w4 = w4 + w3; -// vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); -// -// vy = vx + y; -// w1 = (vy >> 0) & 0xFF; -// w2 = (vy >> 8) & 0xFF; -// w3 = (vy >> 16) & 0xFF; -//// w4 = (vy >> 24) & 0xFF; -// w2 = w2 + w1; -// w3 = w3 + w2; -//// w4 = w4 + w3; -// vy = (p4Table[w1 & 0xff] ) | (p5Table[w2 & 0xff] ) | (p6Table[w3 & 0xff] ); -// -// // Return a value from -1 to 1. The value of vy is in the range: [0, 2^24 - 1] -// return 1.0 - (vy/8388607.5); - } - - - - - - - - - - - - - - @Override - public double getValue3D(double x, double y, double z) - { - return getInterpolatedValue3D(x * freq, y * freq, z * freq) * amp; - } - - - public double getInterpolatedValue3D(double x, double y, double z) - { - int iX, iY, iZ; - double fracX, fracY, fracZ; - double tL, tR, bL, bR, topRow, botRow; - double planeZ1, planeZ2; - - iX = (int)floor(x); - fracX = x - iX; - fracX = myFracTransformer.transform(fracX); - - iY = (int)floor(y); - fracY = y - iY; - fracY = myFracTransformer.transform(fracY); - - iZ = (int)floor(z); - fracZ = z - iZ; - fracZ = myFracTransformer.transform(fracZ); - - tL = getSmoothValue3D(iX, iY, iZ); - tR = getSmoothValue3D(iX + 1, iY, iZ); - bL = getSmoothValue3D(iX, iY + 1, iZ); - bR = getSmoothValue3D(iX + 1, iY + 1, iZ); - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - planeZ1 = interpolate(topRow, botRow, fracY); - - tL = getSmoothValue3D(iX, iY, iZ + 1); - tR = getSmoothValue3D(iX + 1, iY, iZ + 1); - bL = getSmoothValue3D(iX, iY + 1, iZ + 1); - bR = getSmoothValue3D(iX + 1, iY + 1, iZ + 1); - topRow = interpolate(tL, tR, fracX); - botRow = interpolate(bL, bR, fracX); - planeZ2 = interpolate(topRow, botRow, fracY); - - return interpolate(planeZ1, planeZ2, fracZ); - } - - - public double getSmoothValue3D(int x, int y, int z) - { -return getRawValue3D(x, y, z); -/* double center, surface, side, corner; - - center = getRawValue3D(x, y, z); - center = center / 8; - - surface = getRawValue3D(x - 1, y, z) + getRawValue3D(x + 1, y, z) - + getRawValue3D(x, y - 1, z) + getRawValue3D(x, y + 1, z) - + getRawValue3D(x, y, z - 1) + getRawValue3D(x, y, z + 1); - surface = surface / 16; - - side = getRawValue3D(x, y-1, z-1) + getRawValue3D(x, y-1, z+1) + getRawValue3D(x, y+1, z-1) + getRawValue3D(x, y+1, z+1) - + getRawValue3D(x-1, y, z-1) + getRawValue3D(x-1, y, z+1) + getRawValue3D(x+1, y, z-1) + getRawValue3D(x+1, y, z+1) - + getRawValue3D(x-1, y-1, z) + getRawValue3D(x-1, y+1, z) + getRawValue3D(x+1, y-1, z) + getRawValue3D(x+1, y+1, z); - side = side / 32; - - corner = getRawValue3D(x-1, y-1, z-1) + getRawValue3D(x+1, y-1, z-1) - + getRawValue3D(x-1, y+1, z-1) + getRawValue3D(x+1, y+1, z-1) - + getRawValue3D(x-1, y-1, z+1) + getRawValue3D(x+1, y-1, z+1) - + getRawValue3D(x-1, y+1, z+1) + getRawValue3D(x+1, y+1, z+1); - corner = corner / 64; - - return center + surface + side + corner; -*/ } - - - public double getRawValue3D(int x, int y, int z) - { - int vx, vy, vz; -// int w1, w2, w3, w4; - int w1, w2, w3; - - vx = x; - w1 = (vx >> 0) & 0xFF; - w2 = (vx >> 8) & 0xFF; - w3 = (vx >> 16) & 0xFF; -// w4 = (vx >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vx = (p0Table[w1 & 0xff] ) | (p1Table[w2 & 0xff] ) | (p2Table[w3 & 0xff] ); - - vy = vx + y; - w1 = (vy >> 0) & 0xFF; - w2 = (vy >> 8) & 0xFF; - w3 = (vy >> 16) & 0xFF; -// w4 = (vy >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vy = (p4Table[w1 & 0xff] ) | (p5Table[w2 & 0xff] ) | (p6Table[w3 & 0xff] ); - - vz = vy + z; - w1 = (vz >> 0) & 0xFF; - w2 = (vz >> 8) & 0xFF; - w3 = (vz >> 16) & 0xFF; -// w4 = (vz >> 24) & 0xFF; - w2 = w2 + w1; - w3 = w3 + w2; -// w4 = w4 + w3; - vz = (p8Table[w1 & 0xff] ) | (p9Table[w2 & 0xff] ) | (p10Table[w3 & 0xff] ); - - // Return a value from -1 to 1. The value of vy is in the range: [0, 2^24 - 1] - return 1.0 - (vz/8388607.5); - } - - - /** - * interpolate - Returns a value between v1 and v2 wrt to frac. - */ - protected double interpolate(double v1, double v2, double frac) - { - return v1 + frac*(v2 - v1); - } - -} - diff --git a/src/glum/math/ViolentNoise1.java.misc b/src/glum/math/ViolentNoise1.java.misc deleted file mode 100644 index 8b3f2a3..0000000 --- a/src/glum/math/ViolentNoise1.java.misc +++ /dev/null @@ -1,79 +0,0 @@ -package glum.math; - -import java.util.*; - -public class ViolentNoise1 implements Noise -{ - // Seed key values - protected Collection myNoiseList; - - - /** - * Constructor - */ - public ViolentNoise1() - { - myNoiseList = new LinkedList(); - } - - - /** - * add - */ - public void add(Noise aNoise) - { - myNoiseList.add(aNoise); - } - - - /** - * getValue1D - Returns a continous random value in the range of [0-1] in - * 1D space. It is repeatable for the same x values. - */ - public double getValue1D(double x) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue1D(x); - - total = Math.sin(x + total); - return total; - } - - - /** - * getValue2D - Returns a continous random value in the range of [0-1] in - * 2D space. It is repeatable for the same x,y values. - */ - public double getValue2D(double x, double y) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue2D(x, y); - - total = Math.sin(x + total); - return total; - } - - - /** - * getValue3D - Returns a continous random value in the range of [0-1] in - * 3D space. It is repeatable for the same x,y,z values. - */ - public double getValue3D(double x, double y, double z) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += aNoiseGen.getValue3D(x, y, z); - - total = Math.sin(x + total); - return total; - } - -} diff --git a/src/glum/math/ViolentNoise2.java.misc b/src/glum/math/ViolentNoise2.java.misc deleted file mode 100644 index a2c75ec..0000000 --- a/src/glum/math/ViolentNoise2.java.misc +++ /dev/null @@ -1,79 +0,0 @@ -package glum.math; - -import java.util.*; - -public class ViolentNoise2 implements Noise -{ - // Seed key values - protected Collection myNoiseList; - - - /** - * Constructor - */ - public ViolentNoise2() - { - myNoiseList = new LinkedList(); - } - - - /** - * add - */ - public void add(Noise aNoise) - { - myNoiseList.add(aNoise); - } - - - /** - * getValue1D - Returns a continous random value in the range of [0-1] in - * 1D space. It is repeatable for the same x values. - */ - public double getValue1D(double x) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += Math.abs(aNoiseGen.getValue1D(x)); - - total = Math.sin(x + total); - return total; - } - - - /** - * getValue2D - Returns a continous random value in the range of [0-1] in - * 2D space. It is repeatable for the same x,y values. - */ - public double getValue2D(double x, double y) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += Math.abs(aNoiseGen.getValue2D(x, y)); - - total = Math.sin(x + total); - return total; - } - - - /** - * getValue3D - Returns a continous random value in the range of [0-1] in - * 3D space. It is repeatable for the same x,y,z values. - */ - public double getValue3D(double x, double y, double z) - { - double total; - - total = 0; - for (Noise aNoiseGen : myNoiseList) - total += Math.abs(aNoiseGen.getValue3D(x, y, z)); - - total = Math.tan(x + total); - return total; - } - -} diff --git a/src/glum/misc/InitListener.java b/src/glum/misc/InitListener.java new file mode 100644 index 0000000..0f6044b --- /dev/null +++ b/src/glum/misc/InitListener.java @@ -0,0 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.misc; + +/** + * Listener that provides notification whenever a source has completed initialization. + * + * @author lopeznr1 + */ +public interface InitListener +{ + + /** + * Method that is called whenever the specified source has been initialized. + * + * @param aSource + */ + public void handleInitAction(Object aSource); + +} diff --git a/src/glum/net/Credential.java b/src/glum/net/Credential.java index 1e6d94d..b433a30 100644 --- a/src/glum/net/Credential.java +++ b/src/glum/net/Credential.java @@ -1,5 +1,23 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.net; +/** + * Defines credentials needed to access a resource. + * + * @author lopeznr1 + */ public class Credential { // Constants @@ -11,12 +29,14 @@ public class Credential protected final char[] password; /** + * Standard Constructor + *

    * Creates a domainless credential. - * + * * @param aUsername * @param aPassword - * the password to utilize. Note: a reference to this memory is retained by design to minimize distribution - * of the password content through the application. + * the password to utilize. Note: a reference to this memory is retained by design to minimize distribution of + * the password content through the application. */ public Credential(String aUsername, char[] aPassword) { @@ -43,7 +63,7 @@ public class Credential /** * Returns the password character array. - * + * * @return a reference to the internal array held by the instance. */ public char[] getPassword() diff --git a/src/glum/net/FetchError.java b/src/glum/net/FetchError.java new file mode 100644 index 0000000..8d44561 --- /dev/null +++ b/src/glum/net/FetchError.java @@ -0,0 +1,47 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.net; + +/** + * Custom {@link Exception} that is thrown whenever a download fails. + * + * @author lopeznr1 + * + */ +public class FetchError extends Exception +{ + // Attributes + private final Result refResult; + + /** + * Standard Constructor + */ + public FetchError(Throwable aCause, Result aResult) + { + super(aCause); + + refResult = aResult; + } + + /** + * Returns the {@link Result} + * + * @return + */ + public Result getResult() + { + return refResult; + } + +} diff --git a/src/glum/net/HostUtil.java b/src/glum/net/HostUtil.java new file mode 100644 index 0000000..7b91d50 --- /dev/null +++ b/src/glum/net/HostUtil.java @@ -0,0 +1,144 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.net; + +import java.io.InputStream; +import java.io.InputStreamReader; + +import com.google.common.base.Charsets; +import com.google.common.io.CharStreams; + +/** + * Collection of utility methods to support determining the system's host name. + * + * @author lopeznr1 + */ +public class HostUtil +{ + // Constants + /** + * Regex for host name. Source: + * https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address + */ + private static final String ValidHostnameRegex = "^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$"; + + // Cache vars (static) + private static String cHostName = null; + + /** + * Returns the system host name as specified via the following: + *

      + *
    • First try the relevant system environment variable. + *
    • Query the system via the hostname command + *
    + * Returns null on failure. + *

    + * Source: https://stackoverflow.com/questions/7348711/recommended-way-to-get-hostname-in-java + * + * @param aAllowCache + * If set to true, then the cached version will be utilized (if available) otherwise a system call may be + * required. + */ + public static String getHostName(boolean aAllowCache) + { + String hostName = cHostName; + if (aAllowCache == true && hostName != null) + return hostName; + + String osName = System.getProperty("os.name").toLowerCase(); + if (osName.contains("win")) + { + hostName = System.getenv("COMPUTERNAME"); + if (hostName == null) + hostName = execReadToString("hostname"); + } + else if (osName.contains("nix") || osName.contains("nux") || osName.contains("mac os x")) + { + hostName = System.getenv("HOSTNAME"); + if (hostName == null) + hostName = execReadToString("hostname"); + if (hostName == null) + hostName = execReadToString("cat /etc/hostname"); + } + + // Remove extra whitespace + if (hostName != null) + hostName = hostName.trim(); + + // Transform empty string to null + if (hostName == null || hostName.isEmpty() == true) + hostName = null; + + // Update the cache + cHostName = hostName; + + return hostName; + } + + /** + * Returns the system host name as specified via the following: + *

      + *
    • First try the relevant system environment variable. + *
    • Query the system via the hostname command + *
    + * Returns null on failure. + *

    + * Note that this method will return cached values. + */ + public static String getHostName() + { + // Delegate + return getHostName(true); + } + + /** + * Utility method that returns true if the specified host name is valid. + */ + public static boolean isValidHostName(String aName) + { + if (aName == null) + return false; + + return aName.matches(ValidHostnameRegex); + } + + /** + * Return the string corresponding to the executed command. + *

    + * On success returns the corresponding string with whitespace stripped. + *

    + * Any failure will result in null. + */ + private static String execReadToString(String execCommand) + { + try + { + Process tmpProcess = Runtime.getRuntime().exec(execCommand); + try (InputStream aStream = tmpProcess.getInputStream()) + { + String result = CharStreams.toString(new InputStreamReader(aStream, Charsets.UTF_8)); + return result.trim(); + } + catch (Exception aExp) + { + return null; + } + } + catch (Exception aExp) + { + return null; + } + } + +} diff --git a/src/glum/net/NetUtil.java b/src/glum/net/NetUtil.java index 51ef553..852293f 100644 --- a/src/glum/net/NetUtil.java +++ b/src/glum/net/NetUtil.java @@ -1,17 +1,250 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.net; -import java.io.BufferedInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.ConnectException; -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; -import java.net.UnknownHostException; +import java.io.*; +import java.net.*; +import java.security.DigestInputStream; +import java.security.MessageDigest; import java.util.Base64; +import glum.digest.Digest; +import glum.digest.DigestUtils; +import glum.io.IoUtil; +import glum.task.ConsoleTask; +import glum.task.Task; +import glum.unit.NumberUnit; + +/** + * Collection of utility methods for working with remote resources. + * + * @author lopeznr1 + */ public class NetUtil { + /** + * Utility method to download the specified file from aSrcUrl to aDestFile. Returns true on success. + *

    + * Note the passed in {@link Task}'s progress will be updated from 0% to 100% at file download completion, If the + * specified parameter aFileSize is invalid (aFileSize <= 0) or the download turns out to be bigger than the + * specified size then there will be no progress update while the file is being downloaded - only at completion. + *

    + * This method can be aborted via {@link Task#abort()}. + *

    + *

    + * Returns true if the download is successfully completed. Returns false if the provided task is aborted. + */ + public static boolean download(Task aTask, URL aSrcUrl, File aDstFile, Credential aCredential, long aFileSize, + MessageDigest aDigest, String aUpdateMsg) throws FetchError + { + + NumberUnit perNU = new NumberUnit("", "", 1.0, "0.00 %"); + + // Ensure we have a valid aTask + if (aTask == null) + aTask = new ConsoleTask(); + + // Allocate space for the byte buffer + byte[] byteArr = new byte[10000]; + + // Determine if the file download should be "resumed" + long cntByteCurr = 0L; + if (aDstFile.exists() == true) + cntByteCurr = aDstFile.length(); + + // Perform the actual copying + InputStream inStream = null; + OutputStream outStream = null; + URLConnection connection = null; + try + { + // Open the src stream (with a 30 sec connect timeout) + connection = aSrcUrl.openConnection(); + if (cntByteCurr > 0) + connection.setRequestProperty("Range", "bytes=" + cntByteCurr + "-"); +// connection.setConnectTimeout(30 * 1000); +// connection.setReadTimeout(90 * 1000); + + // Open the InputStream + inStream = NetUtil.getInputStream(connection, aCredential); + if (aDigest != null) + inStream = new DigestInputStream(inStream, aDigest); + + // Open the OutputStream + boolean isAppend = false; + if (cntByteCurr > 0) + isAppend = true; + outStream = new FileOutputStream(aDstFile, isAppend); + + // Copy the bytes from the instream to the outstream + long cntByteFull = aFileSize; + int numBytes = 0; + while (numBytes != -1) + { + numBytes = inStream.read(byteArr); + if (numBytes > 0) + { + outStream.write(byteArr, 0, numBytes); + cntByteCurr += numBytes; + } + + // Update the progressVal to reflect the download progress. Note however that we do update the + // progress to 100% since that would change the task to be flagged as inactive and thus cause + // the download to be aborted prematurely. + // TODO: In the future Tasks should not be marked as inactive based on progress values + double progressVal = 0; + if (cntByteFull > 0) + { + progressVal = (cntByteCurr + 0.0) / cntByteFull; + if (progressVal >= 1.0) + progressVal = 0.99; + } + + if (aUpdateMsg != null) + aTask.logRegUpdate(aUpdateMsg + perNU.getString(progressVal) + "\n"); + aTask.setProgress(progressVal); + + // Bail if the Task has been aborted + if (aTask.isAborted() == true) + return false; + } + + // Mark aTask's progress as complete since the file was downloaded. + if (aUpdateMsg != null) + aTask.logRegUpdate(aUpdateMsg + perNU.getString(1.0) + "\n"); + aTask.setProgress(1.0); + } + catch (IOException aExp) + { + Result tmpResult = getResult(aExp, connection); + throw new FetchError(aExp, tmpResult); + } + finally + { + IoUtil.forceClose(inStream); + IoUtil.forceClose(outStream); + } + + return true; + } + + /** + * Utility method to download the specified file from aSrcUrl to aDestFile. Returns true on success. + *

    + * Note the passed in {@link Task}'s progress will be updated from 0% to 100% at file download completion, If the + * specified parameter aFileSize is invalid (aFileSize <= 0) or the download turns out to be bigger than the + * specified size then there will be no progress update while the file is being downloaded - only at completion. + *

    + * On any error logging will be sent to the provided Task. + *

    + * Validation of the download will be performed if a valid {@link Digest} (aTargDigest) is provided. + *

    + * This method can be aborted via {@link Task#abort()}. + *

    + * Returns true if the download is successfully completed. Returns false if the provided task is aborted. + */ + public static boolean download(Task aTask, URL aSrcUrl, File aDstFile, Credential aCredential, long aFileLen, + Digest aTargDigest) + { + // Form the message digest of interest + MessageDigest tmpMessageDigest = null; + if (aTargDigest != null) + tmpMessageDigest = DigestUtils.getDigest(aTargDigest.getType()); + + try + { + NetUtil.download(aTask, aSrcUrl, aDstFile, aCredential, aFileLen, tmpMessageDigest, null); + if (aTask.isAborted() == true) + { + aTask.logRegln("File download has been aborted..."); + aTask.logRegln("\tSource: " + aSrcUrl); + aTask.logRegln("\tFile: " + aDstFile + "\n"); +// aTask.logRegln("\tFile: " + dstFile + " Bytes transferred: " + cntByteCurr); + return false; + } + } + catch (FetchError aExp) + { + aTask.logRegln("File download has failed..."); + aTask.logRegln("\tReason: " + aExp.getResult()); + aTask.logRegln("\tSource: " + aSrcUrl); + aTask.logRegln("\tFile: " + aDstFile + "\n"); + return false; + } + + // Success if there is no targDigest to validate against + if (aTargDigest == null) + return true; + + // Validate that the file was downloaded successfully + Digest testDigest = new Digest(aTargDigest.getType(), tmpMessageDigest.digest()); + if (aTargDigest.equals(testDigest) == false) + { + aTask.logRegln("File download is corrupted..."); + aTask.logRegln("\tFile: " + aDstFile); + aTask.logRegln("\t\tExpected " + aTargDigest.getDescr()); + aTask.logRegln("\t\tReceived " + testDigest.getDescr() + "\n"); + return false; + } + + return true; + } + + /** + * Utility method that converts an IOException to an understandable message + */ + public static String getErrorCodeMessage(URL aUpdateUrl, URLConnection aConnection, IOException aExp) + { + // Form a user friendly exception + String errMsg; + errMsg = "The update site, " + aUpdateUrl + ", is not available.\n\t"; + + Result result; + result = NetUtil.getResult(aExp, aConnection); + switch (result) + { + case BadCredentials: + errMsg += "The update site is password protected and bad credentials were provided.\n"; + break; + + case ConnectFailure: + case UnreachableHost: + case UnsupportedConnection: + errMsg += "The update site appears to be unreachable.\n"; + break; + + case Interrupted: + errMsg += "The retrival of the remote file has been interrupted.\n"; + break; + + case InvalidResource: + errMsg += "The remote file does not appear to be valid.\n"; + break; + + default: + errMsg += "An undefined error occurred while retrieving the remote file.\n"; + break; + } + + // Log the URL which we failed on + URL fetchUrl; + fetchUrl = aConnection.getURL(); + errMsg += "\tURL: " + fetchUrl + "\n"; + + return errMsg; + } + /** * Utility method for retrieving the response code */ @@ -21,7 +254,7 @@ public class NetUtil { return aConnection.getResponseCode(); } - catch(IOException aExp) + catch (IOException aExp) { return -1; } @@ -65,7 +298,7 @@ public class NetUtil // See if there was a problem with the HTTP Connection if (aConnection instanceof HttpURLConnection) { - int responseCode = getResponseCode((HttpURLConnection)aConnection); + int responseCode = getResponseCode((HttpURLConnection) aConnection); switch (responseCode) { case HttpURLConnection.HTTP_UNAUTHORIZED: @@ -104,7 +337,7 @@ public class NetUtil /** * Checks to see whether the supplied aCredential is valid for the specified root URI. - * + * * @return The result of testing the credentials. */ public static Result checkCredentials(String uriRoot, Credential aCredential) @@ -129,7 +362,7 @@ public class NetUtil inStream.close(); return Result.Success; } - catch(Exception aExp) + catch (Exception aExp) { // aExp.printStackTrace(); return getResult(aExp, aConnection); diff --git a/src/glum/net/Result.java b/src/glum/net/Result.java index c59da4e..128c8c3 100644 --- a/src/glum/net/Result.java +++ b/src/glum/net/Result.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.net; public enum Result diff --git a/src/glum/net/UrlUtil.java b/src/glum/net/UrlUtil.java new file mode 100644 index 0000000..7b6bcf5 --- /dev/null +++ b/src/glum/net/UrlUtil.java @@ -0,0 +1,123 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.net; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.net.URL; +import java.nio.charset.StandardCharsets; + +import com.google.common.io.ByteStreams; + +import glum.task.Task; +import glum.util.ThreadUtil; + +/** + * Collection of utility methods for working with {@link URL}s. + * + * @author lopeznr1 + */ +public class UrlUtil +{ + /** + * utility method that returns a the textual content corresponding to the resource at the specified URL. + *

    + * Returns null on failure, and logs the exception to aTask. + */ + public static String readText(Task aTask, URL aUrl) + { + try (InputStream aStream = aUrl.openStream()) + { + // Utilize Java 9 Stream read facility +// return new String(aStream.readAllBytes(), StandardCharsets.UTF_8); + byte[] tmpByteArr = ByteStreams.toByteArray(aStream); + return new String(tmpByteArr, StandardCharsets.UTF_8); + } + catch (IOException aExp) + { + aTask.logRegln(ThreadUtil.getStackTrace(aExp)); + } + + return null; + } + + /** + * Transforms a string representation of url to a {@link URL}. + *

    + * On any error a wrapped {@link RuntimeException} will be thrown. + */ + public static URL toUrl(String aUrlStr) + { + try + { + return new URL(aUrlStr); + } + catch (Exception aExp) + { + throw new RuntimeException(aExp); + } + } + + /** + * Transforms a {@link URI} to a {@link URL}. + *

    + * On any error a wrapped {@link RuntimeException} will be thrown. + */ + public static URL toUrl(URI aURI) + { + try + { + return aURI.toURL(); + } + catch (Exception aExp) + { + throw new RuntimeException(aExp); + } + } + + /** + * Transforms and resolves a list of strings into a URL. + *

    + * The first string should be absolute and reference some top level folder. All intermediate strings will be + * interpreted as relative folders. Resolving is done via {@link URI#resolve(String)} + *

    + * Minimal error checking is done so it is the responsibility of the caller to ensure input is valid. Wrapped + * {@link RuntimeException}s will be thrown. + */ + public static URL resolve(String... aStrArr) + { + String tmpStr = aStrArr[0]; + if (tmpStr.endsWith("/") == false) + tmpStr += "/"; + URI tmpURI = URI.create(tmpStr); + + for (int c1 = 0; c1 < aStrArr.length - 1; c1++) + { + tmpStr = aStrArr[c1]; + if (tmpStr.endsWith("/") == false) + tmpStr = tmpStr += '/'; + + tmpURI = tmpURI.resolve(tmpStr); + } + + int lastIdx = aStrArr.length - 1; + if (lastIdx > 0) + tmpURI = tmpURI.resolve(aStrArr[lastIdx]); + + // Delegate + return toUrl(tmpURI); + } + +} diff --git a/src/glum/reflect/Function.java b/src/glum/reflect/Function.java index 31f56fd..1490383 100644 --- a/src/glum/reflect/Function.java +++ b/src/glum/reflect/Function.java @@ -5,9 +5,9 @@ import java.lang.reflect.Method; /** * Class to provide easy access to function and allow them to be manipulated as objects. - *

    - * Source: http://stackoverflow.com/questions/1073358/function-pointers-in-java
    - * Source: http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html
    + *

    + * Source: http://stackoverflow.com/questions/1073358/function-pointers-in-java
    + * Source: http://tutorials.jenkov.com/java-reflection/private-fields-and-methods.html
    * Source: http://java.sun.com/developer/technicalArticles/ALT/Reflection/ */ public class Function diff --git a/src/glum/reflect/ReflectUtil.java b/src/glum/reflect/ReflectUtil.java index 18cbb80..6ebae3f 100644 --- a/src/glum/reflect/ReflectUtil.java +++ b/src/glum/reflect/ReflectUtil.java @@ -1,66 +1,85 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.reflect; import java.io.File; -import java.lang.reflect.*; -import java.net.URL; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; import java.net.URLDecoder; import javax.lang.model.type.NullType; import com.google.common.primitives.Primitives; +/** + * Collection of reflect related utility methods. + * + * @author lopeznr1 + */ public class ReflectUtil { /** * Utility method to check if an object of type rightType can be assigned to leftType - *

    + *

    * If the leftType is a non primitive then rightType must be null or of a child class. - *

    + *

    * If the leftType is a primitive then this method will only return true if the rightType is a primitive and does not - * need to be cast so that loss of information will occur. I.E:
    - * checkAssignability(long, Int) -> true
    + * need to be cast so that loss of information will occur. I.E:
    + * checkAssignability(long, Int) -> true
    * checkAssignability(int, Long) -> false */ - static boolean checkAssignability(Class leftType, Class rightType) + static boolean checkAssignability(Class aLeftType, Class aRightType) { // Evaluate non primitive assignments - if (leftType.isPrimitive() == false) + if (aLeftType.isPrimitive() == false) { - if (rightType == null || rightType == NullType.class) + if (aRightType == null || aRightType == NullType.class) return true; else - return leftType.isAssignableFrom(rightType); + return aLeftType.isAssignableFrom(aRightType); } // Evaluate primitive assignments - if (rightType == null) + if (aRightType == null) return false; - leftType = Primitives.wrap(leftType); - rightType = Primitives.wrap(rightType); + aLeftType = Primitives.wrap(aLeftType); + aRightType = Primitives.wrap(aRightType); // Bail if same types - if (leftType == rightType) + if (aLeftType == aRightType) return true; - if (leftType == Double.class) + if (aLeftType == Double.class) { - if (rightType == Float.class) + if (aRightType == Float.class) return true; } - else if (leftType == Long.class) + else if (aLeftType == Long.class) { - if (rightType == Integer.class || rightType == Short.class || rightType == Character.TYPE || rightType == Byte.class) + if (aRightType == Integer.class || aRightType == Short.class || aRightType == Character.TYPE + || aRightType == Byte.class) return true; } - else if (leftType == Integer.class) + else if (aLeftType == Integer.class) { - if (rightType == Short.class || rightType == Character.TYPE || rightType == Byte.class) + if (aRightType == Short.class || aRightType == Character.TYPE || aRightType == Byte.class) return true; } - else if (leftType == Short.class) + else if (aLeftType == Short.class) { - if (rightType == Character.TYPE || rightType == Byte.class) + if (aRightType == Character.TYPE || aRightType == Byte.class) return true; } @@ -71,38 +90,32 @@ public class ReflectUtil * Returns the Constructor of aClass with arguments matching parmTypes. On failure it returns null rather than throw * an exception. */ - public static Constructor getConstructorSafe(Class aClass, Class... parmTypes) + public static Constructor getConstructorSafe(Class aClass, Class... aParmTypeArr) { - Constructor aConstructor; - try { - aConstructor = aClass.getConstructor(parmTypes); + return aClass.getConstructor(aParmTypeArr); } - catch(Exception aExp) + catch (Exception aExp) { return null; } - - return aConstructor; } /** * Returns a neatly formatted string corresponding to classArr */ - public static String getStringForClassArray(Class... classArr) + public static String getStringForClassArray(Class... aClassArr) { - String retStr; - - retStr = "["; - for (int c1 = 0; c1 < classArr.length; c1++) + var retStr = "["; + for (int c1 = 0; c1 < aClassArr.length; c1++) { - if (classArr[c1] == null) + if (aClassArr[c1] == null) retStr += "null"; else - retStr += classArr[c1].getName(); + retStr += aClassArr[c1].getName(); - if (c1 + 1 != classArr.length) + if (c1 + 1 != aClassArr.length) retStr += ", "; } retStr += "]"; @@ -110,33 +123,58 @@ public class ReflectUtil } + /** + * Utility method to return the root directory of the specified class. + */ + public static File getInstalledRootDir(Class aClass) + { + File rootDir; + + // Delegate the retrieval of the (schema) root directory + var dataPath = getPartialRootDir(aClass); + + // Remove the jar file component from the path "!*.jar" + var tmpIdx = dataPath.lastIndexOf("!"); + if (tmpIdx != -1) + { + dataPath = dataPath.substring(0, tmpIdx); + rootDir = new File(dataPath); + rootDir = rootDir.getParentFile(); + } + else + { + System.out.println("Warning: " + aClass.getSimpleName() + + " class does not appear to be packaged. Assuming developers environment."); + rootDir = new File(dataPath); + rootDir = rootDir.getParentFile().getParentFile().getParentFile(); + } + + return rootDir; + } + /** * Attempts to return an object of type G1 from the fully qualified path */ - public static G1 loadObject(String aFullClassPath, Class retType) + public static G1 loadObject(String aFullClassPath, Class aRetType) { - Class aClass; - Object aObject; - // Insanity check if (aFullClassPath == null) return null; - aObject = null; try { - aClass = Class.forName(aFullClassPath); - if (retType.isAssignableFrom(aClass) == false) + var tmpClass = Class.forName(aFullClassPath); + if (aRetType.isAssignableFrom(tmpClass) == false) return null; - aObject = aClass.getDeclaredConstructor().newInstance(); - return retType.cast(aObject); + var tmpObject = tmpClass.getDeclaredConstructor().newInstance(); + return aRetType.cast(tmpObject); } - catch(ClassNotFoundException aExp) + catch (ClassNotFoundException aExp) { System.out.println("Failure: " + aFullClassPath + " not found."); } - catch(Exception aExp) + catch (Exception aExp) { // Unknown Exception aExp.printStackTrace(); @@ -149,37 +187,33 @@ public class ReflectUtil * Attempts to return an object of type G1 from the fully qualified path. The object will be constructed using the * vars parmTypes and parmValues. */ - public static G1 loadObject(String aFullClassPath, Class retType, Class[] parmTypes, Object[] parmValues) + public static G1 loadObject(String aFullClassPath, Class aRetType, Class[] aParmTypeArr, + Object[] aParmValueArr) { - Class aClass; - Constructor aConstructor; - Object aObject; - // Insanity check if (aFullClassPath == null) return null; - aObject = null; try { - aClass = Class.forName(aFullClassPath); - if (retType.isAssignableFrom(aClass) == false) + var tmpClass = Class.forName(aFullClassPath); + if (aRetType.isAssignableFrom(tmpClass) == false) return null; // Try to obtain the preferred constructor - aConstructor = aClass.getConstructor(parmTypes); - if (aConstructor == null) + var tmpConstructor = tmpClass.getConstructor(aParmTypeArr); + if (tmpConstructor == null) return null; // Construct the object - aObject = aConstructor.newInstance(parmValues); - return retType.cast(aObject); + var tmpObject = tmpConstructor.newInstance(aParmValueArr); + return aRetType.cast(tmpObject); } - catch(ClassNotFoundException aExp) + catch (ClassNotFoundException aExp) { System.out.println("Failure: " + aFullClassPath + " not found."); } - catch(Exception aExp) + catch (Exception aExp) { // Unknown Exception aExp.printStackTrace(); @@ -188,90 +222,26 @@ public class ReflectUtil return null; } - /** - * Utility method to determine where refClass is installed - */ - public static File getInstalledRootDir(Class refClass) - { - String dataPath; - File rootDir; - URL aUrl; - int aIndex; - - // Attempt to determine the default data directory - aUrl = refClass.getResource(refClass.getSimpleName() + ".class"); - // System.out.println("URL:" + aUrl); - try - { - dataPath = aUrl.toURI().toString(); - dataPath = URLDecoder.decode(dataPath, "UTF-8"); - } - catch(Exception aExp) - { - dataPath = aUrl.getPath(); - try - { - dataPath = URLDecoder.decode(dataPath, "UTF-8"); - } - catch(Exception aExp2) - { - ; - } - } - - // Remove the "file:" protocol specification - aIndex = dataPath.indexOf("file:"); - if (aIndex != -1) - dataPath = dataPath.substring(aIndex + 5); - else - System.out.println("Warning: Protocol \"file:\" not found. Run from http???"); - - // Remove the jar file component from the path "!*.jar" - aIndex = dataPath.lastIndexOf("!"); - if (aIndex != -1) - { - dataPath = dataPath.substring(0, aIndex); - rootDir = new File(dataPath); - rootDir = rootDir.getParentFile(); - } - else - { - System.out.println("Warning: " + refClass.getSimpleName() + " class does not appear to be packaged. Assuming developers environment."); - rootDir = new File(dataPath); - rootDir = rootDir.getParentFile().getParentFile().getParentFile(); - } - - return rootDir; - } - /** * Utility method that searches for a matching method from the specified class, refClass. If null is specified as any * of the parameter types, then any non primitive object will be considered a match for that parameter. */ - public static Method locateMatchingMethod(Class refClass, String methodName, Class... methodParamTypeArr) + public static Method locateMatchingMethod(Class aClass, String aMethodName, Class... aMethodParamTypeArr) { - Method[] methodArr; - boolean isMatch; - Class aClass; - // Search all of the available methods - methodArr = refClass.getDeclaredMethods(); + var methodArr = aClass.getDeclaredMethods(); for (Method aItem : methodArr) { // Ensure the name matches - if (methodName.equals(aItem.getName()) == true) + if (aMethodName.equals(aItem.getName()) == true) { - Class[] evalParamTypeArr; - // Ensure the number of arguments matches - evalParamTypeArr = aItem.getParameterTypes(); - if (evalParamTypeArr.length == methodParamTypeArr.length) + var evalParamTypeArr = aItem.getParameterTypes(); + if (evalParamTypeArr.length == aMethodParamTypeArr.length) { - isMatch = true; + var isMatch = true; for (int c1 = 0; c1 < evalParamTypeArr.length; c1++) - { - isMatch &= checkAssignability(evalParamTypeArr[c1], methodParamTypeArr[c1]); - } + isMatch &= checkAssignability(evalParamTypeArr[c1], aMethodParamTypeArr[c1]); // Found a matching method if (isMatch == true) @@ -281,12 +251,70 @@ public class ReflectUtil } // Try the super classes - aClass = refClass.getSuperclass(); - if (aClass != null) - return locateMatchingMethod(aClass, methodName, methodParamTypeArr); + var tmpClass = aClass.getSuperclass(); + if (tmpClass != null) + return locateMatchingMethod(tmpClass, aMethodName, aMethodParamTypeArr); // No method found return null; } + /** + * Utility method to determine if the JVM is running from a developers environment. + *

    + * Pass the class in of the calling code. + */ + public static boolean isDeveloperEnvironment(Class aClass) + { + // Delegate the retrieval of the (schema) root directory + var dataPath = getPartialRootDir(aClass); + + // If there is a jar ("!*.jar") component then we are packaged up + var tmpIdx = dataPath.lastIndexOf("!"); + if (tmpIdx != -1) + return false; + + return true; + } + + /** + * Utility helper method that returns a string representing the the (schema) root directory of the specified class. + *

    + * If the software is packaged into a jar this string will contain the reference to the jar component. + */ + private static String getPartialRootDir(Class aClass) + { + String dataPath; + + // Attempt to determine the default data directory + var tmpUrl = aClass.getResource(aClass.getSimpleName() + ".class"); + // System.out.println("URL:" + aUrl); + try + { + dataPath = tmpUrl.toURI().toString(); + dataPath = URLDecoder.decode(dataPath, "UTF-8"); + } + catch (Exception aExp) + { + dataPath = tmpUrl.getPath(); + try + { + dataPath = URLDecoder.decode(dataPath, "UTF-8"); + } + catch (Exception aExp2) + { + ; + } + } + + // Remove the "file:" protocol specification + var tmpIndex = dataPath.indexOf("file:"); + if (tmpIndex != -1) + dataPath = dataPath.substring(tmpIndex + 5); + else + System.out.println("Warning: Protocol \"file:\" not found. Run from http???"); + + return dataPath; + } + } diff --git a/src/glum/registry/ConfigMap.java b/src/glum/registry/ConfigMap.java index 5e02358..7df9044 100644 --- a/src/glum/registry/ConfigMap.java +++ b/src/glum/registry/ConfigMap.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.registry; import java.util.*; diff --git a/src/glum/registry/Registry.java b/src/glum/registry/Registry.java index 27217c4..0af6ca8 100644 --- a/src/glum/registry/Registry.java +++ b/src/glum/registry/Registry.java @@ -1,31 +1,47 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.registry; import java.util.*; /** * The Registry class allows for various types of entities to be linked together by a common key. Outside operators can: - *

      - *
    • 1. Add items associated with the key - *
    • 2. Retrieve a collection of all items associated with the key - *
    • 3. Register to receive notification of any changes associated with a key - *
    • 4. Send notification with regard to a key - *
    + *
      + *
    • 1. Add items associated with the key + *
    • 2. Retrieve a collection of all items associated with the key + *
    • 3. Register to receive notification of any changes associated with a key + *
    • 4. Send notification with regard to a key + *
    + * + * @author lopeznr1 */ public class Registry { // State var - private Map> mySetMap; - private Map> mySetListeners; - private Map mySingletonMap; - private Map> mySingletonListeners; + private Map> myGroupM; + private Map> myGroupListenerM; + private Map mySingletonM; + private Map> mySingletonListenerM; + /** Standard Constructor */ public Registry() { - mySetMap = new LinkedHashMap>(); - mySetListeners = new LinkedHashMap>(); + myGroupM = new LinkedHashMap>(); + myGroupListenerM = new LinkedHashMap>(); - mySingletonMap = new LinkedHashMap(); - mySingletonListeners = new LinkedHashMap>(); + mySingletonM = new LinkedHashMap(); + mySingletonListenerM = new LinkedHashMap>(); } /** @@ -33,26 +49,26 @@ public class Registry */ public synchronized void dispose() { - if (mySetMap != null) + if (myGroupM != null) { - mySetMap.clear(); - mySetMap = null; + myGroupM.clear(); + myGroupM = null; } - if (mySetListeners != null) + if (myGroupListenerM != null) { - mySetListeners.clear(); - mySetListeners = null; + myGroupListenerM.clear(); + myGroupListenerM = null; } - if (mySingletonMap != null) + if (mySingletonM != null) { - mySingletonMap.clear(); - mySingletonMap = null; + mySingletonM.clear(); + mySingletonM = null; } - if (mySingletonListeners != null) + if (mySingletonListenerM != null) { - mySingletonListeners.clear(); - mySingletonListeners = null; + mySingletonListenerM.clear(); + mySingletonListenerM = null; } } @@ -61,17 +77,17 @@ public class Registry */ public void addAllResourceItems(Object aKey, Collection aList) { - Collection aCollection; + Collection tmpGroupC; - synchronized(this) + synchronized (this) { - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) { - aCollection = new LinkedHashSet(); - mySetMap.put(aKey, aCollection); + tmpGroupC = new LinkedHashSet(); + myGroupM.put(aKey, tmpGroupC); } - aCollection.addAll(aList); + tmpGroupC.addAll(aList); } // Notify the listeners @@ -83,17 +99,17 @@ public class Registry */ public void addResourceItem(Object aKey, Object aObject) { - Collection aCollection; + Collection tmpGroupC; - synchronized(this) + synchronized (this) { - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) { - aCollection = new LinkedHashSet(); - mySetMap.put(aKey, aCollection); + tmpGroupC = new LinkedHashSet(); + myGroupM.put(aKey, tmpGroupC); } - aCollection.add(aObject); + tmpGroupC.add(aObject); } // Notify the listeners @@ -105,17 +121,17 @@ public class Registry */ public void addResourceListener(Object aKey, ResourceListener aListener) { - Collection aSet; + Collection tmpListenerC; - synchronized(this) + synchronized (this) { - aSet = mySetListeners.get(aKey); - if (aSet == null) + tmpListenerC = myGroupListenerM.get(aKey); + if (tmpListenerC == null) { - aSet = new ArrayList(); - mySetListeners.put(aKey, aSet); + tmpListenerC = new ArrayList(); + myGroupListenerM.put(aKey, tmpListenerC); } - aSet.add(aListener); + tmpListenerC.add(aListener); } } @@ -124,17 +140,17 @@ public class Registry */ public void addSingletonListener(Object aKey, ResourceListener aListener) { - Collection aSet; + Collection tmpListenerC; - synchronized(this) + synchronized (this) { - aSet = mySingletonListeners.get(aKey); - if (aSet == null) + tmpListenerC = mySingletonListenerM.get(aKey); + if (tmpListenerC == null) { - aSet = new ArrayList(); - mySingletonListeners.put(aKey, aSet); + tmpListenerC = new ArrayList(); + mySingletonListenerM.put(aKey, tmpListenerC); } - aSet.add(aListener); + tmpListenerC.add(aListener); } } @@ -143,30 +159,30 @@ public class Registry */ public synchronized List getResourceItems(Object aKey) { - Collection aCollection; + Collection tmpGroupC; - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) return new ArrayList(); - return new ArrayList(aCollection); + return new ArrayList(tmpGroupC); } - public synchronized List getResourceItems(Object aKey, Class retType) + public synchronized List getResourceItems(Object aKey, Class aRetType) { - Collection aCollection; + Collection tmpGroupC; List retList; retList = new ArrayList(); - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) return retList; - for (Object aObj : aCollection) + for (Object aObj : tmpGroupC) { - if (retType.isInstance(aObj) == true) - retList.add(retType.cast(aObj)); + if (aRetType.isInstance(aObj) == true) + retList.add(aRetType.cast(aObj)); } return retList; @@ -177,27 +193,27 @@ public class Registry */ public synchronized Object getSingleton(Object aKey) { - Object aSingleton; + Object tmpSingleton; // Insanity check if (aKey == null) return null; - aSingleton = mySingletonMap.get(aKey); - return aSingleton; + tmpSingleton = mySingletonM.get(aKey); + return tmpSingleton; } - public synchronized G1 getSingleton(Object aKey, Class retType) + public synchronized G1 getSingleton(Object aKey, Class aRetType) { - Object aSingleton; + Object tmpSingleton; // Insanity check if (aKey == null) return null; - aSingleton = mySingletonMap.get(aKey); - if (retType.isInstance(aSingleton) == true) - return retType.cast(aSingleton); + tmpSingleton = mySingletonM.get(aKey); + if (aRetType.isInstance(tmpSingleton) == true) + return aRetType.cast(tmpSingleton); return null; } @@ -207,18 +223,18 @@ public class Registry */ public void removeAllResourceItems(Object aKey) { - Collection aCollection; + Collection tmpGroupC; // Remove the item - synchronized(this) + synchronized (this) { // Get the associated collection - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) return; // Remove all the items from the collection - aCollection.clear(); + tmpGroupC.clear(); } // Notify the listeners @@ -230,18 +246,18 @@ public class Registry */ public void removeResourceItem(Object aKey, Object aObject) { - Collection aCollection; + Collection tmpGroupC; // Remove the item - synchronized(this) + synchronized (this) { // Get the associated collection - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) return; // Remove the item from the collection - aCollection.remove(aObject); + tmpGroupC.remove(aObject); } // Notify the listeners @@ -253,18 +269,18 @@ public class Registry */ public void replaceResourceItems(Object aKey, Collection aList) { - Collection aCollection; + Collection tmpGroupC; - synchronized(this) + synchronized (this) { - aCollection = mySetMap.get(aKey); - if (aCollection == null) + tmpGroupC = myGroupM.get(aKey); + if (tmpGroupC == null) { - aCollection = new LinkedHashSet(); - mySetMap.put(aKey, aCollection); + tmpGroupC = new LinkedHashSet(); + myGroupM.put(aKey, tmpGroupC); } - aCollection.clear(); - aCollection.addAll(aList); + tmpGroupC.clear(); + tmpGroupC.addAll(aList); } // Notify the listeners @@ -276,19 +292,19 @@ public class Registry */ public void removeResourceListener(Object aKey, ResourceListener aListener) { - Collection aSet; + Collection tmpListenerC; // Remove the listenr - synchronized(this) + synchronized (this) { - if (mySetListeners == null) + if (myGroupListenerM == null) return; - aSet = mySetListeners.get(aKey); - if (aSet == null) + tmpListenerC = myGroupListenerM.get(aKey); + if (tmpListenerC == null) return; - aSet.remove(aListener); + tmpListenerC.remove(aListener); } } @@ -297,19 +313,19 @@ public class Registry */ public void removeSingletonListener(Object aKey, ResourceListener aListener) { - Collection aSet; + Collection tmpListenerC; // Remove the listenr - synchronized(this) + synchronized (this) { - if (mySingletonListeners == null) + if (mySingletonListenerM == null) return; - aSet = mySingletonListeners.get(aKey); - if (aSet == null) + tmpListenerC = mySingletonListenerM.get(aKey); + if (tmpListenerC == null) return; - aSet.remove(aListener); + tmpListenerC.remove(aListener); } } @@ -318,14 +334,14 @@ public class Registry */ public void setSingleton(Object aKey, Object aObject) { - synchronized(this) + synchronized (this) { // Remove the entry from the hashtable if null if (aObject == null) - mySingletonMap.remove(aKey); + mySingletonM.remove(aKey); // Set in the entry for the corresponding aKey else - mySingletonMap.put(aKey, aObject); + mySingletonM.put(aKey, aObject); } // Notify the listeners @@ -337,20 +353,20 @@ public class Registry */ public void notifyResourceListeners(Object aKey) { - Collection aSet, notifySet; + Collection tmpListenerC; // Get the listeners - synchronized(this) + synchronized (this) { - aSet = mySetListeners.get(aKey); - if (aSet == null) + tmpListenerC = myGroupListenerM.get(aKey); + if (tmpListenerC == null) return; - notifySet = new ArrayList(aSet); + tmpListenerC = new ArrayList(tmpListenerC); } // Send out the notifications - for (ResourceListener aListener : notifySet) + for (ResourceListener aListener : tmpListenerC) aListener.resourceChanged(this, aKey); } @@ -359,20 +375,20 @@ public class Registry */ public void notifySingletonListeners(Object aKey) { - Collection aSet, notifySet; + Collection tmpListenerC; // Get the listeners - synchronized(this) + synchronized (this) { - aSet = mySingletonListeners.get(aKey); - if (aSet == null) + tmpListenerC = mySingletonListenerM.get(aKey); + if (tmpListenerC == null) return; - notifySet = new ArrayList(aSet); + tmpListenerC = new ArrayList(tmpListenerC); } // Send out the notifications - for (ResourceListener aListener : notifySet) + for (ResourceListener aListener : tmpListenerC) aListener.resourceChanged(this, aKey); } diff --git a/src/glum/registry/ResourceListener.java b/src/glum/registry/ResourceListener.java index ac28bdc..9467887 100644 --- a/src/glum/registry/ResourceListener.java +++ b/src/glum/registry/ResourceListener.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.registry; public interface ResourceListener diff --git a/src/glum/registry/SelectionListener.java b/src/glum/registry/SelectionListener.java index 9c8381d..957eb06 100644 --- a/src/glum/registry/SelectionListener.java +++ b/src/glum/registry/SelectionListener.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.registry; public interface SelectionListener diff --git a/src/glum/registry/SelectionManager.java b/src/glum/registry/SelectionManager.java index aa285fc..a494b39 100644 --- a/src/glum/registry/SelectionManager.java +++ b/src/glum/registry/SelectionManager.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.registry; import java.util.*; @@ -8,23 +21,28 @@ import com.google.common.collect.*; /** * Class that support arbitrary item selection. All valid types of selected items must be registered in the Constructor. + * + * @author lopeznr1 */ public class SelectionManager { // State vars - private Multimap, Object> selectionMap; - private Multimap, SelectionListener> listenerMap; - private Set> registerSet; + private Multimap, Object> selectionM; + private Multimap, SelectionListener> listenerM; + private Set> registerS; private boolean notifyViaAwtThread; - public SelectionManager(Class... classArr) + /** + * Standard Constructor + */ + public SelectionManager(Class... aClassArr) { - selectionMap = ArrayListMultimap.create(); - listenerMap = HashMultimap.create(); + selectionM = ArrayListMultimap.create(); + listenerM = HashMultimap.create(); - registerSet = Sets.newHashSet(); - for (Class aClass : classArr) - registerSet.add(aClass); + registerS = new HashSet<>(); + for (Class aClass : aClassArr) + registerS.add(aClass); notifyViaAwtThread = false; } @@ -35,8 +53,8 @@ public class SelectionManager */ public synchronized void addListener(SelectionListener aListener, Class aClass) { - if (registerSet.contains(aClass) == true) - listenerMap.put(aClass, aListener); + if (registerS.contains(aClass) == true) + listenerM.put(aClass, aListener); else throw new RuntimeException("Unregistered selection class: " + aClass); } @@ -54,13 +72,13 @@ public class SelectionManager */ public void addItem(Class aClass, G1 aItem, SelectionListener aSkipListener) { - if (registerSet.contains(aClass) == false) + if (registerS.contains(aClass) == false) throw new RuntimeException("Unregistered selection class: " + aClass); // Replace the old selections with the new item list - synchronized(this) + synchronized (this) { - selectionMap.put(aClass, aItem); + selectionM.put(aClass, aItem); } notifyListeners(aClass, aSkipListener); @@ -69,23 +87,23 @@ public class SelectionManager /** * Add to the list of selected items associated with aClass */ - public void addItems(Class aClass, List aItemList) + public void addItems(Class aClass, List aItemL) { - addItems(aClass, aItemList, null); + addItems(aClass, aItemL, null); } /** * Adds to the list of selected items and notifies all listeners but the specified skipListener */ - public void addItems(Class aClass, List aItemList, SelectionListener aSkipListener) + public void addItems(Class aClass, List aItemL, SelectionListener aSkipListener) { - if (registerSet.contains(aClass) == false) + if (registerS.contains(aClass) == false) throw new RuntimeException("Unregistered selection class: " + aClass); // Replace the old selections with the new item list - synchronized(this) + synchronized (this) { - selectionMap.putAll(aClass, aItemList); + selectionM.putAll(aClass, aItemL); } notifyListeners(aClass, aSkipListener); @@ -97,36 +115,36 @@ public class SelectionManager @SuppressWarnings("unchecked") public synchronized List getSelectedItems(Class aClass) { - if (registerSet.contains(aClass) == false) + if (registerS.contains(aClass) == false) throw new RuntimeException("Unregistered selection class: " + aClass); - return (List)Lists.newArrayList(selectionMap.get(aClass)); + return (List) new ArrayList<>(selectionM.get(aClass)); } /** * Removes from the list of selected items associated with aClass */ - public void removeItems(Class aClass, List aItemList) + public void removeItems(Class aClass, List aItemL) { - removeItems(aClass, aItemList, null); + removeItems(aClass, aItemL, null); } /** * Removes from the list of selected items and notifies all listeners but the specified skipListener */ - public void removeItems(Class aClass, List aItemList, SelectionListener skipListener) + public void removeItems(Class aClass, List aItemL, SelectionListener skipListener) { - if (registerSet.contains(aClass) == false) + if (registerS.contains(aClass) == false) throw new RuntimeException("Unregistered selection class: " + aClass); // Replace the old selections with the new item list - synchronized(this) + synchronized (this) { Set replaceSet; replaceSet = new LinkedHashSet(getSelectedItems(aClass)); - replaceSet.removeAll(aItemList); - selectionMap.replaceValues(aClass, replaceSet); + replaceSet.removeAll(aItemL); + selectionM.replaceValues(aClass, replaceSet); } notifyListeners(aClass, skipListener); @@ -135,23 +153,23 @@ public class SelectionManager /** * Sets in the selected items and notifies all listeners */ - public void setItems(Class aClass, List aItemList) + public void setItems(Class aClass, List aItemL) { - setItems(aClass, aItemList, null); + setItems(aClass, aItemL, null); } /** * Sets in the selected items and notifies all listeners but the specified skipListener */ - public void setItems(Class aClass, List aItemList, SelectionListener aSkipListener) + public void setItems(Class aClass, List aItemL, SelectionListener aSkipListener) { - if (registerSet.contains(aClass) == false) + if (registerS.contains(aClass) == false) throw new RuntimeException("Unregistered selection class: " + aClass); // Replace the old selections with the new item list - synchronized(this) + synchronized (this) { - selectionMap.replaceValues(aClass, aItemList); + selectionM.replaceValues(aClass, aItemL); } notifyListeners(aClass, aSkipListener); @@ -174,19 +192,18 @@ public class SelectionManager // Ensure this logic is always executed on the AWT thread (if notifyViaAwtThread == true) if (notifyViaAwtThread == true && SwingUtilities.isEventDispatchThread() == false) { - Runnable tmpRunnable = () -> notifyListeners(aClass, aSkipListener); - SwingUtilities.invokeLater(tmpRunnable); + SwingUtilities.invokeLater(() -> notifyListeners(aClass, aSkipListener)); return; } - List listenerList; - synchronized(this) + List listenerL; + synchronized (this) { - listenerList = new ArrayList<>(listenerMap.get(aClass)); - listenerList.remove(aSkipListener); + listenerL = new ArrayList<>(listenerM.get(aClass)); + listenerL.remove(aSkipListener); } - for (SelectionListener aListener : listenerList) + for (SelectionListener aListener : listenerL) aListener.selectionChanged(this, aClass); } diff --git a/src/glum/source/LocalSource.java b/src/glum/source/LocalSource.java new file mode 100644 index 0000000..17b1cad --- /dev/null +++ b/src/glum/source/LocalSource.java @@ -0,0 +1,118 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.source; + +import java.io.File; +import java.net.URL; + +/** + * Local (immutable) implementation of {@link Source}. + *

    + * This object supports only 1 attributes: + *

      + *
    • localFile: The local location where the file can be retrieved (or stored). + *
    + * Note that localFile can not be null. + * + * @author lopeznr1 + */ +public class LocalSource implements Source +{ + // Attributes + private final File localFile; + private final boolean isAuthoritative; + + /** Standard constructor. */ + public LocalSource(File aLocalFile, boolean aIsAuthoritative) + { + localFile = aLocalFile; + isAuthoritative = aIsAuthoritative; + + // Insanity check + if (localFile == null) + throw new NullPointerException("Parameter aLocalFile must not be null."); + } + + /** Simplified constructor. */ + public LocalSource(File aLocalFile) + { + this(aLocalFile, true); + } + + @Override + public long getDiskSize() + { + // If flagged as authoritative then trust the (local) file system and use + // the use the size stored there otherwise no way to know if the value is + // authoritative. + if (isAuthoritative == true) + return localFile.length(); + + return -1; + } + + @Override + public String getName() + { + return localFile.getName(); + } + + @Override + public String getPath() + { + return localFile.getPath(); + } + + @Override + public File getLocalFile() + { + return localFile; + } + + @Override + public URL getRemoteUrl() + { + return null; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + LocalSource other = (LocalSource) obj; + if (localFile == null) + { + if (other.localFile != null) + return false; + } + else if (!localFile.equals(other.localFile)) + return false; + return true; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((localFile == null) ? 0 : localFile.hashCode()); + return result; + } + +} diff --git a/src/glum/source/PlainSource.java b/src/glum/source/PlainSource.java new file mode 100644 index 0000000..57cb430 --- /dev/null +++ b/src/glum/source/PlainSource.java @@ -0,0 +1,140 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.source; + +import java.io.File; +import java.net.URL; + +/** + * Simple (immutable) implementation of {@link Source}. + *

    + * This object supports 2 attributes: + *

      + *
    • remoteUrl: The remote location where the resource can be retrieved. + *
    • localFile: The local location where the file can be retrieved (or stored). + *
    + * Note that either the remoteUrl or localFile can be null but not both. If both are specified then the remoteUrl takes + * precedence. + * + * @author lopeznr1 + */ +public class PlainSource implements Source +{ + // Attributes + private final File localFile; + private final URL remoteUrl; + private final long diskSize; + + /** + * Standard constructor. + *

    + * Either the local or remote resource may be null but not both! + */ + public PlainSource(File aLocalFile, URL aRemoteUrl, long aDiskSize) + { + localFile = aLocalFile; + remoteUrl = aRemoteUrl; + diskSize = aDiskSize; + + // Insanity check + if (localFile == null && remoteUrl == null) + throw new NullPointerException("Both aLocalFile and aRemoteUrl may not be null."); + } + + /** + * Simplified Constructor + */ + public PlainSource(File aLocalFile, URL aRemoteUrl) + { + this(aLocalFile, aRemoteUrl, -1); + } + + @Override + public long getDiskSize() + { + return diskSize; + } + + @Override + public String getName() + { + if (remoteUrl != null) + { + String tmpPath = remoteUrl.getPath(); + int tmpIdx = tmpPath.lastIndexOf("/"); + return tmpPath.substring(tmpIdx + 1); + } + + return localFile.getName(); + } + + @Override + public String getPath() + { + if (remoteUrl != null) + return "" + remoteUrl; + + return localFile.getPath(); + } + + @Override + public File getLocalFile() + { + return localFile; + } + + @Override + public URL getRemoteUrl() + { + return remoteUrl; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PlainSource other = (PlainSource) obj; + if (localFile == null) + { + if (other.localFile != null) + return false; + } + else if (!localFile.equals(other.localFile)) + return false; + if (remoteUrl == null) + { + if (other.remoteUrl != null) + return false; + } + else if (!remoteUrl.equals(other.remoteUrl)) + return false; + return true; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((localFile == null) ? 0 : localFile.hashCode()); + result = prime * result + ((remoteUrl == null) ? 0 : remoteUrl.hashCode()); + return result; + } + +} diff --git a/src/glum/source/Source.java b/src/glum/source/Source.java new file mode 100644 index 0000000..9ed939a --- /dev/null +++ b/src/glum/source/Source.java @@ -0,0 +1,61 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.source; + +import java.io.File; +import java.net.URL; + +/** + * Interface that defines the location of a resource. + *

    + * Implementations of this interface should be immutable. + * + * @author lopeznr1 + */ +public interface Source +{ + /** + * Returns the (expected) disk size (in bytes) of the source. + *

    + * If the size is unknown then -1 should be returned. + */ + public long getDiskSize(); + + /** + * Returns the file name associated with the source. + *

    + * This is just the last element in the path. + */ + public String getName(); + + /** + * Returns the path as a string associated with the source. + */ + public String getPath(); + + /** + * Returns the (local) source. + *

    + * May return null. + */ + public File getLocalFile(); + + /** + * Returns the (remote) source. + *

    + * May return null. + */ + public URL getRemoteUrl(); + +} diff --git a/src/glum/source/SourceState.java b/src/glum/source/SourceState.java new file mode 100644 index 0000000..e4bc705 --- /dev/null +++ b/src/glum/source/SourceState.java @@ -0,0 +1,35 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.source; + +/** + * Enum that defines the current (availability) state of a {@link Source}. + * + * @author lopeznr1 + */ +public enum SourceState +{ + /** Defines the source as being located on a local file system. */ + Local, + + /** Defines the source as being located on a remote system. */ + Remote, + + /** Defines the source as being partially fetched to a local file system. */ + Partial, + + /** Defines the source as being unavailable. */ + Unavailable; + +} diff --git a/src/glum/source/SourceUtil.java b/src/glum/source/SourceUtil.java new file mode 100644 index 0000000..2bf9617 --- /dev/null +++ b/src/glum/source/SourceUtil.java @@ -0,0 +1,277 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.source; + +import java.io.File; +import java.io.IOException; +import java.net.*; + +import glum.net.*; +import glum.task.Task; +import glum.unit.ByteUnit; +import glum.unit.NumberUnit; + +/** + * Collection of utility methods for working with {@link Source} objects. + * + * @author lopeznr1 + */ +public class SourceUtil +{ + /** + * Utility method that will download the contents of the {@link Source}. + *

    + * The contents will be downloaded from the remote location to local file. + * + * @param aTask + * {@link Task} used to monitor or abort the download process. + * @param aSource + * {@link Source} to be downloaded. + * @param aCredential + * User {@link Credential} for the resource. May be null if credentials are not needed. + */ + public static void download(Task aTask, Source aSource, Credential aCredential) throws IOException, FetchError + { + URL remoteUrl = aSource.getRemoteUrl(); + File localFile = aSource.getLocalFile(); + long diskSize = aSource.getDiskSize(); + + // Log the start of the download + File partFile = getTempFile(aSource); + if (partFile.exists() == false) + aTask.logRegln("Downloading file..."); + else + aTask.logRegln("Resuming download..."); + + // Ensure the source has both a local and remote end points + if (localFile == null) + throw new IOException("Local file has not been specified."); + if (remoteUrl == null) + throw new IOException("Remote URL has not been specified."); + + // Ensure the parent folder exists + File parentDir = localFile.getParentFile(); + parentDir.mkdirs(); + + if (parentDir.exists() == false) + throw new IOException("Parent folder does not exist! \n\tFolder: " + parentDir); + if (parentDir.canWrite() == false) + throw new IOException("Parent folder is read only! \n\tFolder: " + parentDir); + + // Fetch the remote file + boolean isPass; + try + { + // Delegate the actual download + String updateMsg = "\tProgress: "; + isPass = NetUtil.download(aTask, remoteUrl, partFile, aCredential, diskSize, null, updateMsg); + } + catch (FetchError aExp) + { + aTask.logRegln("File download has failed..."); + + Result tmpResult = aExp.getResult(); + switch (tmpResult) + { + case BadCredentials: + aTask.logRegln("\tReason: The site is password protected and bad credentials were provided.\n"); + break; + + case ConnectFailure: + case UnreachableHost: + case UnsupportedConnection: + aTask.logRegln("\tReason: The site appears to be unreachable.\n"); + break; + + case Interrupted: + aTask.logRegln("\tReason: The retrival of the remote file has been interrupted.\n"); + break; + + case InvalidResource: + aTask.logRegln("\tReason: The remote file does not appear to be valid..\n"); + break; + + default: + aTask.logRegln("\tReason: Unknown ---> " + tmpResult + "..\n"); + break; + } + + // Rethrow the error + throw aExp; + } + + // Log the results + String resultMsg = "\tDownload has been completed.\n"; + if (isPass == false) + { + resultMsg = "\tDownload has failed!\n"; + if (aTask.isAborted() == true) + resultMsg = "\tDownload has been aborted.\n"; + aTask.abort(); + } + aTask.logRegln(resultMsg); + + // Move the (completed) tmpFile to the proper location + if (isPass == true) + partFile.renameTo(localFile); + } + + /** + * Forms a {@link Source} relevant to the specified parameters. + * + * @param aCacheDir + * The folder where content will be cached + * @param aBasePath + * The base path of the source + * @param aTargPath + * The path to the resource. If basePath is null then this will be interpreted as absolute. + * @param aDiskSize + * The disk size in bytes of the resource. + * + * @throws MalformedURLException + */ + public static Source formSource(File aCacheDir, String aBasePath, String aTargPath, long aDiskSize) + throws MalformedURLException + { + if (aBasePath != null) + { + if (aBasePath.startsWith("http") == true) + { + File localFile = null; + if (aCacheDir != null) + localFile = new File(aCacheDir, aTargPath); + + if (aBasePath.endsWith("/") == false) + aBasePath += "/"; + + URL remotePath = new URL(aBasePath + aTargPath); + return new PlainSource(localFile, remotePath, aDiskSize); + } + + return new LocalSource(new File(aBasePath, aTargPath)); + } + + return new LocalSource(new File(aTargPath)); + } + + /** + * Utility method that returns the file that should be used as the temporal file when it is being fetched. Currently + * this just appends the text ".part" to the file. + */ + public static File getTempFile(Source aSource) + { + File localFile = aSource.getLocalFile(); + if (localFile == null) + return null; + + File retFile = new File(localFile.getParentFile(), localFile.getName() + ".part"); + return retFile; + } + + /** + * Utility method that returns the base path associated with the specified {@link Source}. + *

    + * If both the remote {@link URL} and local {@link File} are specified then the base path will be relative to the + * remote {@link URL}. + */ + public static String getBasePath(Source aSource) + { + URL remoteUrl = aSource.getRemoteUrl(); + if (remoteUrl != null) + { + try + { + return "" + remoteUrl.toURI().resolve("."); + } + catch (URISyntaxException aExp) + { + throw new RuntimeException(aExp); + } + } + + File localFile = aSource.getLocalFile(); + return localFile.getParent(); + } + + /** + * Returns the {@link SourceState} associated with the source. + *

    + * Depending on underlying file system changes the returned state may out dated. + */ + public static SourceState getState(Source aSource) + { + File localFile = aSource.getLocalFile(); + if (localFile != null && localFile.isFile() == true) + return SourceState.Local; + + File partFile = getTempFile(aSource); + if (partFile != null && partFile.isFile() == true) + return SourceState.Partial; + + URL remoteUrl = aSource.getRemoteUrl(); + if (remoteUrl != null) + return SourceState.Remote; + + return SourceState.Unavailable; + } + + /** + * Utility method that returns a detailed message describing the overall status of the {@link Source}. + */ + public static String getStatusMsg(Source aSource) + { + URL remoteUrl = aSource.getRemoteUrl(); + File localFile = aSource.getLocalFile(); + long diskSize = aSource.getDiskSize(); + + File partFile = getTempFile(aSource); + double perDone = partFile.length() / (diskSize + 0.0); + + NumberUnit perNU = new NumberUnit("", "", 1.0, "0.00 %"); + ByteUnit tmpBU = new ByteUnit(2); + + // Log the header + String retMsg = "File has not been fetched."; + if (remoteUrl == null) + retMsg = "File is local."; + else if (localFile.exists() == true) + retMsg = "File has been downloaded and is local."; + else if (partFile.exists() == true) + { + retMsg = "File has been partially downloaded. [Fetched: " + perNU.getString(perDone) + "]"; + if (diskSize < 0) + retMsg = "File has been partially downloaded. [Fetched: " + tmpBU.getString(partFile.length()) + "]"; + } + else + retMsg = "File is remote. A download will be required."; + + // Log the stats + retMsg += "\n"; + if (remoteUrl != null) + { + retMsg += "\tSRC: " + remoteUrl + "\n"; + retMsg += "\tDST: " + localFile + "\n"; + } + else + { + retMsg += "\tName: " + localFile.getName() + "\n"; + retMsg += "\tPath: " + localFile.getParent() + "\n"; + } + if (diskSize > 0) + retMsg += "\tSize: " + tmpBU.getString(diskSize) + "\n"; + + return retMsg; + } + +} diff --git a/src/glum/task/BufferTask.java b/src/glum/task/BufferTask.java new file mode 100644 index 0000000..f244147 --- /dev/null +++ b/src/glum/task/BufferTask.java @@ -0,0 +1,172 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +import com.google.common.base.Strings; + +/** + * Implementation of {@link Task} where all logging is captured to a buffer. + * + * @author lopeznr1 + */ +public class BufferTask implements Task +{ + // State vars + private boolean isAborted; + private boolean isActive; + private double progress; + private String tabStr; + + private String dynamicMsgLast; + private StringBuffer workSB; + + /** Standard Constructor */ + public BufferTask() + { + isAborted = false; + isActive = true; + progress = 0; + tabStr = null; + + dynamicMsgLast = null; + workSB = new StringBuffer(); + } + + /** + * Clears out the buffer. The buffer will return an empty string after this call. + */ + public void clearBuffer() + { + dynamicMsgLast = null; + workSB = new StringBuffer(); + } + + /** + * Return the (full) text sent to this task's buffer. + */ + public String getBuffer() + { + String tmpMsg = dynamicMsgLast; + if (tmpMsg == null) + return workSB.toString(); + + return workSB.toString() + tmpMsg; + } + + @Override + public void abort() + { + isAborted = true; + isActive = false; + } + + @Override + public double getProgress() + { + return progress; + } + + @Override + public boolean isAborted() + { + return isAborted; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + // Force the last dynamic message to output + String dynMsg = dynamicMsgLast; + if (dynMsg != null) + { + if (tabStr != null) + dynMsg = dynMsg.replace("\t", tabStr); + workSB.append(dynMsg); + } + dynamicMsgLast = null; + + String tmpMsg = String.format(aFmtMsg, aObjArr); + + // Update the tab chars + if (tabStr != null) + tmpMsg = tmpMsg.replace("\t", tabStr); + + workSB.append(tmpMsg); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + logReg(aFmtMsg + '\n', aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + // Update the dynamic message + String tmpMsg = String.format(aFmtMsg, aObjArr); + dynamicMsgLast = tmpMsg; + } + + @Override + public void reset() + { + isAborted = false; + isActive = true; + progress = 0; + } + + @Override + public void setProgress(double aProgress) + { + progress = aProgress; + } + + @Override + public void setProgress(int currVal, int maxVal) + { + setProgress((currVal + 0.0) / maxVal); + } + + @Override + public void setRefreshRateMs(long aRateMs) + { + ; // Nothing to do + } + + @Override + public void setStatus(String aStatus) + { + ; // Nothing to do + } + + @Override + public void setTabSize(int numSpaces) + { + tabStr = Strings.repeat(" ", numSpaces); + } + + @Override + public void setTitle(String aTitle) + { + ; // Nothing to do + } + +} diff --git a/src/glum/task/ConsoleTask.java b/src/glum/task/ConsoleTask.java index dc87bdd..8e3259d 100644 --- a/src/glum/task/ConsoleTask.java +++ b/src/glum/task/ConsoleTask.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; import com.google.common.base.Strings; @@ -5,11 +18,18 @@ import com.google.common.base.Strings; import glum.unit.NumberUnit; import glum.unit.Unit; +/** + * Implementation of {@link Task} where all logging will be sent to the console. + * + * @author lopeznr1 + */ public class ConsoleTask implements Task { + // State vars + private boolean isAborted; private boolean isActive; private double progress; - + private String dynamicMsgFrag; private String dynamicMsgLast; private long dynamicMsgRateMs; @@ -17,25 +37,28 @@ public class ConsoleTask implements Task private Unit progressUnit; private boolean showProgressInUpdate; - + private String tabStr; + /** Standard Constructor */ public ConsoleTask(boolean aShowProgressInUpdate) { + isAborted = false; isActive = true; progress = 0; - + dynamicMsgFrag = null; dynamicMsgLast = null; dynamicMsgRateMs = 37; oldTimeMs = Long.MIN_VALUE; - progressUnit = new NumberUnit("", "", 100, 2); + progressUnit = new NumberUnit("", "", 100, 2); showProgressInUpdate = aShowProgressInUpdate; - + tabStr = null; } + /** Simplified Constructor */ public ConsoleTask() { this(false); @@ -44,23 +67,44 @@ public class ConsoleTask implements Task @Override public void abort() { + isAborted = true; isActive = false; } - + @Override - public void infoAppend(String aMsg) + public double getProgress() + { + return progress; + } + + @Override + public boolean isAborted() + { + return isAborted; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) { // Force the last dynamic message to be shown if (dynamicMsgLast != null) dynamicMsgUpdateForce(dynamicMsgLast, 0); dynamicMsgLast = null; - + + String tmpMsg = String.format(aFmtMsg, aObjArr); + // Update the tab chars if (tabStr != null) - aMsg = aMsg.replace("\t", tabStr); - + tmpMsg = tmpMsg.replace("\t", tabStr); + // Display the new message - System.out.print(aMsg); + System.out.print(tmpMsg); System.out.flush(); // Reset the dynamic vars @@ -70,20 +114,22 @@ public class ConsoleTask implements Task } @Override - public void infoAppendln(String aMsg) + public void logRegln(String aFmtMsg, Object... aObjArr) { - infoAppend(aMsg + '\n'); + logReg(aFmtMsg + '\n', aObjArr); } - + @Override - public void infoUpdate(String aMsg) + public void logRegUpdate(String aFmtMsg, Object... aObjArr) { long currTimeMs, totalTimeMs; - + + String tmpMsg = String.format(aFmtMsg, aObjArr); + // Auto add the progress into update messages if necessary - dynamicMsgLast = aMsg; + dynamicMsgLast = tmpMsg; if (showProgressInUpdate == true) - dynamicMsgLast = "[" + progressUnit.getString(getProgress()) +"%] " + aMsg; + dynamicMsgLast = "[" + progressUnit.getString(getProgress()) + "%] " + tmpMsg; // Get the current time currTimeMs = System.nanoTime() / 1000000; @@ -98,15 +144,10 @@ public class ConsoleTask implements Task dynamicMsgLast = null; } - @Override - public double getProgress() - { - return progress; - } - @Override public void reset() { + isAborted = false; isActive = true; progress = 0; } @@ -118,9 +159,9 @@ public class ConsoleTask implements Task } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - setProgress((currVal + 0.0) / maxVal); + setProgress((aCurrVal + 0.0) / aMaxVal); } @Override @@ -137,9 +178,9 @@ public class ConsoleTask implements Task } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { - tabStr = Strings.repeat(" ", numSpaces); + tabStr = Strings.repeat(" ", aNumSpaces); } @Override @@ -149,12 +190,6 @@ public class ConsoleTask implements Task ; // Nothing to do } - @Override - public boolean isActive() - { - return isActive; - } - /** * Helper method that does the actual updating of the previous dynamic text with aMsg. */ @@ -169,7 +204,7 @@ public class ConsoleTask implements Task for (int c1 = 0; c1 < numTempChars; c1++) System.out.print("\b \b"); } - + // Update the tab chars if (tabStr != null) aMsg = aMsg.replace("\t", tabStr); diff --git a/src/glum/task/CountTask.java b/src/glum/task/CountTask.java index 28f3f8b..346967a 100644 --- a/src/glum/task/CountTask.java +++ b/src/glum/task/CountTask.java @@ -1,25 +1,43 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; /** - * Task that automatically updates the progress based on a count. - * Once the currCount == fullCount then the Task is Complete. + * Task that automatically updates the progress based on a count. Once the currCount == fullCount then the Task is + * considered complete. + * + * @author lopeznr1 */ public class CountTask implements Task { - protected Task refTask; - protected int fullCount; - protected int currCount; + // Ref vars + private final Task refTask; + // State vars + private int fullCount; + private int currCount; + + /** Standard Constructor */ public CountTask(Task aRefTask, int aFullCount) { refTask = aRefTask; fullCount = aFullCount; currCount = 0; } - + /** - * Increment the currCount (and corresponding) progress to - * completion. + * Increment the currCount (and corresponding) progress to completion. */ public void incrementCount() { @@ -32,24 +50,6 @@ public class CountTask implements Task { refTask.abort(); } - - @Override - public void infoAppend(String aMsg) - { - refTask.infoAppend(aMsg); - } - - @Override - public void infoAppendln(String aMsg) - { - refTask.infoAppendln(aMsg); - } - - @Override - public void infoUpdate(String aMsg) - { - refTask.infoUpdate(aMsg); - } @Override public double getProgress() @@ -57,6 +57,36 @@ public class CountTask implements Task return (currCount + 0.0) / fullCount; } + @Override + public boolean isAborted() + { + return refTask.isAborted(); + } + + @Override + public boolean isActive() + { + return refTask.isActive(); + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + refTask.logReg(aFmtMsg, aObjArr); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + refTask.logRegln(aFmtMsg, aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + refTask.logRegUpdate(aFmtMsg, aObjArr); + } + @Override public void reset() { @@ -72,9 +102,9 @@ public class CountTask implements Task } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - setProgress((currVal + 0.0)/ maxVal); + setProgress((aCurrVal + 0.0) / aMaxVal); } @Override @@ -90,9 +120,9 @@ public class CountTask implements Task } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { - refTask.setTabSize(numSpaces); + refTask.setTabSize(aNumSpaces); } @Override @@ -101,10 +131,4 @@ public class CountTask implements Task refTask.setTitle(aTitle); } - @Override - public boolean isActive() - { - return refTask.isActive(); - } - } diff --git a/src/glum/task/FileLogTask.java b/src/glum/task/FileLogTask.java deleted file mode 100644 index b1188eb..0000000 --- a/src/glum/task/FileLogTask.java +++ /dev/null @@ -1,166 +0,0 @@ -package glum.task; - -import glum.unit.NumberUnit; -import glum.unit.Unit; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; - -import com.google.common.base.Strings; - -public class FileLogTask implements Task -{ - private boolean isActive; - private double progress; - private String tabStr; - - private String dynamicMsgLast; - private Unit progressUnit; - private boolean showProgressInUpdate; - - private PrintStream printStream; - - public FileLogTask(File aFile, boolean isAppend, boolean aShowProgressInUpdate) - { - isActive = true; - progress = 0; - tabStr = null; - - dynamicMsgLast = null; - progressUnit = new NumberUnit("", "", 100, 2); - showProgressInUpdate = aShowProgressInUpdate; - - // Open the file stream for writing - try - { - printStream = new PrintStream(new FileOutputStream(aFile, isAppend)); - } - catch (FileNotFoundException aExp) - { - throw new RuntimeException(aExp); - } - - } - - public FileLogTask(File aFile, boolean isAppend) - { - this(aFile, isAppend, false); - } - - /** - * Properly close the associated file used for the log - */ - public void close() - { - printStream.close(); - } - - /** - * Configures whether the dynamic messages should have an automatic progress bar readout. - */ - public void setShowProgressInUpdate(boolean aShowProgressInUpdate) - { - showProgressInUpdate = aShowProgressInUpdate; - } - - @Override - public void abort() - { - isActive = false; - } - - @Override - public void infoAppend(String aMsg) - { - // Force the last dynamic message to output - if (dynamicMsgLast != null) - printStream.print(dynamicMsgLast); - dynamicMsgLast = null; - - // Update the tab chars - if (tabStr != null) - aMsg = aMsg.replace("\t", tabStr); - - // Display the new message - printStream.print(aMsg); - printStream.flush(); - } - - @Override - public void infoAppendln(String aMsg) - { - infoAppend(aMsg + '\n'); - } - - @Override - public void infoUpdate(String aMsg) - { - // Update the dynamic message - dynamicMsgLast = aMsg; - - // Auto add the progress into update messages if necessary - if (showProgressInUpdate == true) - dynamicMsgLast = "[" + progressUnit.getString(getProgress()) + "%] " + aMsg; - } - - @Override - public double getProgress() - { - return progress; - } - - @Override - public void reset() - { - isActive = true; - progress = 0; - } - - @Override - public void setProgress(double aProgress) - { - progress = aProgress; - } - - @Override - public void setProgress(int currVal, int maxVal) - { - setProgress((currVal + 0.0) / maxVal); - } - - @Override - public void setRefreshRateMs(long aRateMs) - { - // Refresh rate is nonsensical for a file log - ; // Nothing to do - } - - @Override - public void setStatus(String aStatus) - { - // FileLogTasks do not support a status line. - ; // Nothing to do - } - - @Override - public void setTabSize(int numSpaces) - { - tabStr = Strings.repeat(" ", numSpaces); - } - - @Override - public void setTitle(String aTitle) - { - // FileLogTasks do not support a title line. - ; // Nothing to do - } - - @Override - public boolean isActive() - { - return isActive; - } - -} diff --git a/src/glum/task/IndentTask.java b/src/glum/task/IndentTask.java new file mode 100644 index 0000000..4968395 --- /dev/null +++ b/src/glum/task/IndentTask.java @@ -0,0 +1,153 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +/** + * Task designed to encompass 1 child task and indent all output by the specified number of tabs. + * + * @author lopeznr1 + */ +public class IndentTask implements Task +{ + // Attributes + private final Task childTask; + private final String tabStr; + + // State vars + private boolean isTrailNL; + + /** Standard Constructor */ + public IndentTask(Task aChildTask, int aNumIndent) + { + childTask = aChildTask; + + String tmpStr = ""; + for (int c1 = 0; c1 < aNumIndent; c1++) + tmpStr += "\t"; + tabStr = tmpStr; + + isTrailNL = true; + } + + @Override + public void abort() + { + childTask.abort(); + } + + @Override + public double getProgress() + { + return childTask.getProgress(); + } + + @Override + public boolean isAborted() + { + return childTask.isAborted(); + } + + @Override + public boolean isActive() + { + return childTask.isActive(); + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + String tmpMsg = String.format(aFmtMsg, aObjArr); + + // Indent the first log message on the following conditions: + // - The previous output had the last char == '\n' (a trailing NL) + // - If aMsg does NOT start with a newline + if (isTrailNL == true && tmpMsg.startsWith("\n") == false) + { + isTrailNL = false; + tmpMsg = tabStr + tmpMsg; + } + + // Keep track of any trailing newline and strip it from aMsg + if (tmpMsg.endsWith("\n") == true) + { + isTrailNL = true; + tmpMsg = tmpMsg.substring(0, tmpMsg.length() - 1); + } + + // Indent any text that is after a newline + tmpMsg = tmpMsg.replaceAll("\n", "\n" + tabStr); + + // Add back in the trailing newline + if (isTrailNL == true) + tmpMsg += "\n"; + + childTask.logReg(tmpMsg); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + logReg(aFmtMsg + "\n", aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + aFmtMsg = aFmtMsg.replaceAll("\n", "\n" + tabStr); + childTask.logRegUpdate(aFmtMsg, aObjArr); + } + + @Override + public void reset() + { + childTask.reset(); + } + + @Override + public void setProgress(double aProgress) + { + childTask.setProgress(aProgress); + } + + @Override + public void setProgress(int aCurrVal, int aMaxVal) + { + childTask.setProgress(aCurrVal, aMaxVal); + } + + @Override + public void setRefreshRateMs(long aRateMs) + { + childTask.setRefreshRateMs(aRateMs); + } + + @Override + public void setStatus(String aStatus) + { + childTask.setStatus(aStatus); + } + + @Override + public void setTabSize(int aNumSpaces) + { + childTask.setTabSize(aNumSpaces); + } + + @Override + public void setTitle(String aTitle) + { + childTask.setTitle(aTitle); + } + +} diff --git a/src/glum/task/InvalidTask.java b/src/glum/task/InvalidTask.java new file mode 100644 index 0000000..6bacb3f --- /dev/null +++ b/src/glum/task/InvalidTask.java @@ -0,0 +1,121 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +/** + * Singleton instance of {@link Task} that defines the "invalid" {@link Task}. + *

    + * All attempts to change the state of this task will be ignored. + *

    + * All queries to this task will reflect that of an inactive / aborted task. + * + * @author lopeznr1 + */ +public class InvalidTask implements Task +{ + // Constants + /** The "invalid" {@link Task}. */ + static final InvalidTask Instance = new InvalidTask(); + + /** Private Singleton Constructor */ + private InvalidTask() + { + ; // Nothing to do + } + + @Override + public void abort() + { + ; // Nothing to do + } + + @Override + public double getProgress() + { + return Double.NaN; + } + + @Override + public boolean isAborted() + { + return true; + } + + @Override + public boolean isActive() + { + return false; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + + @Override + public void reset() + { + ; // Nothing to do + } + + @Override + public void setProgress(double aProgress) + { + ; // Nothing to do + } + + @Override + public void setProgress(int aCurrVal, int aMaxVal) + { + ; // Nothing to do + } + + @Override + public void setRefreshRateMs(long aRateMs) + { + ; // Nothing to do + } + + @Override + public void setStatus(String aStatus) + { + ; // Nothing to do + } + + @Override + public void setTabSize(int aNumSpaces) + { + ; // Nothing to do + } + + @Override + public void setTitle(String aTitle) + { + ; // Nothing to do + } + +} diff --git a/src/glum/task/NotifyTask.java b/src/glum/task/NotifyTask.java new file mode 100644 index 0000000..0baacf7 --- /dev/null +++ b/src/glum/task/NotifyTask.java @@ -0,0 +1,255 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +/** + * Implementation of {@link Task} where notification of progress updates is made via a single {@link TaskListener}. + *

    + * Notifications will be sent out once no more frequently than once every 47 milliseconds (~21 updates per second). The + * frequency of notifications can be configured via {@link #setRefreshRateMS(long)} + * + * @author lopeznr1 + */ +public class NotifyTask implements Task +{ + // Ref vars + private final Task refTask; + private final TaskListener refListener; + + // State vars + private boolean isInit; + private long begTime; + private long endTime; + + private long refreshRateMs; + + /** Standard Constructor. */ + public NotifyTask(Task aTask, TaskListener aListener) + { + refTask = aTask; + refListener = aListener; + + isInit = true; + begTime = 0L; + endTime = 0L; + + refreshRateMs = 47; + } + + /** Simplified Constructor. */ + public NotifyTask(TaskListener aListener) + { + this(new SilentTask(), aListener); + } + + /** + * Forces an update to this task. + *

    + * If the task is active then the end time will be updated. + *

    + * The {@link TaskListener} will be notified. + */ + public void forceUpdate() + { + if (isActive() == true) + endTime = System.currentTimeMillis(); + + refListener.taskUpdate(this); + } + + /** + * Return true if the task has reached completion or has been aborted. + */ + public boolean isDone() + { + if (isAborted() == true) + return true; + + if (getProgress() >= 1.0) + return true; + + return false; + } + + /** + * Return true if the task has not left the initial state. + *

    + * A task should be considered in the initial state if the progress has not moved past 0% and / or if it has not been + * manually transitioned out. + */ + public boolean isInit() + { + return isInit; + } + + /** + * Moves the task from the init state to the in-progress state + */ + public void markInitDone() + { + begTime = System.currentTimeMillis(); + isInit = false; + } + + /** + * Returns the reference {@link Task}. + */ + public Task getRefTask() + { + return refTask; + } + + /** + * Returns the rate (in milliseconds) at which notifications will be sent out. + */ + public long getRefreshRateMS() + { + return refreshRateMs; + } + + /** + * Returns the (system) time when this Task was reset or initialization was completed. + */ + public long getTimeBeg() + { + return begTime; + } + + /** + * Returns the (system) time when this Task was last updated. + */ + public long getTimeEnd() + { + return endTime; + } + + @Override + public void abort() + { + if (isActive() == true) + endTime = System.currentTimeMillis(); + + refTask.abort(); + + isInit = false; + refListener.taskUpdate(this); + } + + @Override + public double getProgress() + { + return refTask.getProgress(); + } + + @Override + public boolean isAborted() + { + return refTask.isAborted(); + } + + @Override + public boolean isActive() + { + return refTask.isActive(); + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + refTask.logReg(aFmtMsg, aObjArr); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + refTask.logRegln(aFmtMsg, aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + refTask.logRegUpdate(aFmtMsg, aObjArr); + } + + @Override + public void reset() + { + refTask.reset(); + + isInit = true; + begTime = System.currentTimeMillis(); + endTime = 0L; + } + + @Override + public void setProgress(double aProgress) + { + refTask.setProgress(aProgress); + isInit = false; + + // Update UI at refreshRateMS + long currTime = System.currentTimeMillis(); + if (currTime < endTime + refreshRateMs) + return; + + // Update + if (isActive() == true) + endTime = System.currentTimeMillis(); + + refListener.taskUpdate(this); + } + + @Override + public void setProgress(int aCurrVal, int aMaxVal) + { + refTask.setProgress(aCurrVal, aMaxVal); + isInit = false; + + // Update UI at refreshRateMS + long currTime = System.currentTimeMillis(); + if (currTime < endTime + refreshRateMs) + return; + + // Update + if (isActive() == true) + endTime = System.currentTimeMillis(); + + refListener.taskUpdate(this); + } + + @Override + public void setRefreshRateMs(long aRateMs) + { + refreshRateMs = aRateMs; + } + + @Override + public void setStatus(String aStatus) + { + refTask.setStatus(aStatus); + } + + @Override + public void setTabSize(int aNumSpaces) + { + refTask.setTabSize(aNumSpaces); + } + + @Override + public void setTitle(String aTitle) + { + refTask.setTitle(aTitle); + } + +} diff --git a/src/glum/task/PartialTask.java b/src/glum/task/PartialTask.java index 12557a9..ab92aec 100644 --- a/src/glum/task/PartialTask.java +++ b/src/glum/task/PartialTask.java @@ -1,12 +1,35 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; +/** + * Task designed to be a partial of a larger {@link Task}. Utilization of this task is focused on the smaller partial + * task at hand. + * + * @author lopeznr1 + */ public class PartialTask implements Task { - protected Task refTask; - protected double progressOffset; - protected double progressTotalFragment; - protected double internalProgress; + // Ref vars + private final Task refTask; + // State vars + private double progressOffset; + private double progressTotalFragment; + private double internalProgress; + + /** Standard Constructor */ public PartialTask(Task aRefTask, double aProgressOffset, double aProgressTotalFragment) { refTask = aRefTask; @@ -21,30 +44,42 @@ public class PartialTask implements Task refTask.abort(); } - @Override - public void infoAppend(String aMsg) - { - refTask.infoAppend(aMsg); - } - - @Override - public void infoAppendln(String aMsg) - { - refTask.infoAppendln(aMsg); - } - - @Override - public void infoUpdate(String aMsg) - { - refTask.infoUpdate(aMsg); - } - @Override public double getProgress() { return progressOffset + (progressTotalFragment * internalProgress); } + @Override + public boolean isAborted() + { + return refTask.isAborted(); + } + + @Override + public boolean isActive() + { + return refTask.isActive(); + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + refTask.logReg(aFmtMsg, aObjArr); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + refTask.logRegln(aFmtMsg, aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + refTask.logRegUpdate(aFmtMsg, aObjArr); + } + @Override public void reset() { @@ -61,9 +96,9 @@ public class PartialTask implements Task } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - setProgress((currVal + 0.0) / maxVal); + setProgress((aCurrVal + 0.0) / aMaxVal); } @Override @@ -79,9 +114,9 @@ public class PartialTask implements Task } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { - refTask.setTabSize(numSpaces); + refTask.setTabSize(aNumSpaces); } @Override @@ -90,10 +125,4 @@ public class PartialTask implements Task refTask.setTitle(aTitle); } - @Override - public boolean isActive() - { - return refTask.isActive(); - } - } diff --git a/src/glum/task/PrintStreamTask.java b/src/glum/task/PrintStreamTask.java new file mode 100644 index 0000000..4de841b --- /dev/null +++ b/src/glum/task/PrintStreamTask.java @@ -0,0 +1,188 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +import java.io.PrintStream; + +import com.google.common.base.Strings; + +import glum.unit.NumberUnit; +import glum.unit.Unit; + +/** + * Implementation of {@link Task} where all logging will be sent to the provided {@link PrintStream}. + * + * @author lopeznr1 + */ +public class PrintStreamTask implements Task +{ + // Ref vars + private final PrintStream refStream; + + // State vars + private boolean isAborted; + private boolean isActive; + private double progress; + private String tabStr; + + private String dynamicMsgLast; + private Unit progressUnit; + private boolean showProgressInUpdate; + + /** + * Standard Constructor + * + * @param aStream + * @param aShowProgressInUpdate + */ + public PrintStreamTask(PrintStream aStream, boolean aShowProgressInUpdate) + { + refStream = aStream; + + isAborted = false; + isActive = true; + progress = 0; + tabStr = null; + + dynamicMsgLast = null; + progressUnit = new NumberUnit("", "", 100, 2); + showProgressInUpdate = aShowProgressInUpdate; + } + + /** + * Properly close the associated file used for the log + */ + public void close() + { + refStream.close(); + } + + /** + * Configures whether the dynamic messages should have an automatic progress bar readout. + */ + public void setShowProgressInUpdate(boolean aShowProgressInUpdate) + { + showProgressInUpdate = aShowProgressInUpdate; + } + + @Override + public void abort() + { + isAborted = true; + isActive = false; + } + + @Override + public double getProgress() + { + return progress; + } + + @Override + public boolean isAborted() + { + return isAborted; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + // Force the last dynamic message to output + if (dynamicMsgLast != null) + refStream.print(dynamicMsgLast); + dynamicMsgLast = null; + + String tmpMsg = String.format(aFmtMsg, aObjArr); + + // Update the tab chars + if (tabStr != null) + tmpMsg = tmpMsg.replace("\t", tabStr); + + // Display the new message + refStream.print(tmpMsg); + refStream.flush(); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + logReg(aFmtMsg + '\n', aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + // Update the dynamic message + String tmpMsg = String.format(aFmtMsg, aObjArr); + dynamicMsgLast = tmpMsg; + + // Auto add the progress into update messages if necessary + if (showProgressInUpdate == true) + dynamicMsgLast = "[" + progressUnit.getString(getProgress()) + "%] " + tmpMsg; + } + + @Override + public void reset() + { + isAborted = false; + isActive = true; + progress = 0; + } + + @Override + public void setProgress(double aProgress) + { + progress = aProgress; + } + + @Override + public void setProgress(int aCurrVal, int aMaxVal) + { + setProgress((aCurrVal + 0.0) / aMaxVal); + } + + @Override + public void setRefreshRateMs(long aRateMs) + { + // Refresh rate is nonsensical for a file log + ; // Nothing to do + } + + @Override + public void setStatus(String aStatus) + { + // PrintStreamTasks do not support a status line. + ; // Nothing to do + } + + @Override + public void setTabSize(int aNumSpaces) + { + tabStr = Strings.repeat(" ", aNumSpaces); + } + + @Override + public void setTitle(String aTitle) + { + // PrintStreamTasks do not support a title line. + ; // Nothing to do + } + +} diff --git a/src/glum/task/SilentTask.java b/src/glum/task/SilentTask.java index e6d5a66..4c1e2aa 100644 --- a/src/glum/task/SilentTask.java +++ b/src/glum/task/SilentTask.java @@ -1,12 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; +/** + * Implementation of {@link Task} where all logging is ignored. + * + * @author lopeznr1 + */ public class SilentTask implements Task { + // State vars + private boolean isAborted; private boolean isActive; private double progress; + /** Standard Constructor */ public SilentTask() { + isAborted = false; isActive = true; progress = 0; } @@ -14,36 +36,50 @@ public class SilentTask implements Task @Override public void abort() { + isAborted = true; isActive = false; } - @Override - public void infoAppend(String aMsg) - { - ; // Nothing to do - } - - @Override - public void infoAppendln(String aMsg) - { - ; // Nothing to do - } - - @Override - public void infoUpdate(String aMsg) - { - ; // Nothing to do - } - @Override public double getProgress() { return progress; } + @Override + public boolean isAborted() + { + return isAborted; + } + + @Override + public boolean isActive() + { + return isActive; + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + ; // Nothing to do + } + @Override public void reset() { + isAborted = false; isActive = true; progress = 0; } @@ -55,9 +91,9 @@ public class SilentTask implements Task } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - setProgress((currVal + 0.0) / maxVal); + setProgress((aCurrVal + 0.0) / aMaxVal); } @Override @@ -73,7 +109,7 @@ public class SilentTask implements Task } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { ; // Nothing to do } @@ -84,10 +120,4 @@ public class SilentTask implements Task ; // Nothing to do } - @Override - public boolean isActive() - { - return isActive; - } - } diff --git a/src/glum/task/SplitTask.java b/src/glum/task/SplitTask.java index dffb83e..0617798 100644 --- a/src/glum/task/SplitTask.java +++ b/src/glum/task/SplitTask.java @@ -1,117 +1,137 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; -import java.util.List; - -import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; /** * Task designed to encompass many "sub" child tasks and forward actions to each child task. - *

    + *

    * On any query method call such as {@link #getProgress()}, the first child task specified in the constructor will be * utilized as the task to fulfill the query. + * + * @author lopeznr1 */ public class SplitTask implements Task { - private List childTaskList; + // Attributes + private final ImmutableList childTaskL; /** + * Standard Constructor + * * @param aTaskArr - * The list of child tasks for which this SplitTask will automatically forward the method calls to. + * The list of child tasks for which this SplitTask will automatically forward the method calls to. */ public SplitTask(Task... aTaskArr) { - childTaskList = Lists.newLinkedList(); - for (Task aTask : aTaskArr) - childTaskList.add(aTask); + childTaskL = ImmutableList.copyOf(aTaskArr); } @Override public void abort() { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.abort(); } - @Override - public void infoAppend(String aMsg) - { - for (Task aTask : childTaskList) - aTask.infoAppend(aMsg); - } - - @Override - public void infoAppendln(String aMsg) - { - for (Task aTask : childTaskList) - aTask.infoAppendln(aMsg); - } - - @Override - public void infoUpdate(String aMsg) - { - for (Task aTask : childTaskList) - aTask.infoUpdate(aMsg); - } - @Override public double getProgress() { - return childTaskList.get(0).getProgress(); + return childTaskL.get(0).getProgress(); + } + + @Override + public boolean isAborted() + { + return childTaskL.get(0).isAborted(); + } + + @Override + public boolean isActive() + { + return childTaskL.get(0).isActive(); + } + + @Override + public void logReg(String aFmtMsg, Object... aObjArr) + { + for (Task aTask : childTaskL) + aTask.logReg(aFmtMsg, aObjArr); + } + + @Override + public void logRegln(String aFmtMsg, Object... aObjArr) + { + for (Task aTask : childTaskL) + aTask.logRegln(aFmtMsg, aObjArr); + } + + @Override + public void logRegUpdate(String aFmtMsg, Object... aObjArr) + { + for (Task aTask : childTaskL) + aTask.logRegUpdate(aFmtMsg, aObjArr); } @Override public void reset() { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.reset(); } @Override public void setProgress(double aProgress) { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.setProgress(aProgress); } @Override - public void setProgress(int currVal, int maxVal) + public void setProgress(int aCurrVal, int aMaxVal) { - for (Task aTask : childTaskList) - aTask.setProgress(currVal, maxVal); + for (Task aTask : childTaskL) + aTask.setProgress(aCurrVal, aMaxVal); } @Override public void setRefreshRateMs(long aRateMs) { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.setRefreshRateMs(aRateMs); } @Override public void setStatus(String aStatus) { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.setStatus(aStatus); } @Override - public void setTabSize(int numSpaces) + public void setTabSize(int aNumSpaces) { - for (Task aTask : childTaskList) - aTask.setTabSize(numSpaces); + for (Task aTask : childTaskL) + aTask.setTabSize(aNumSpaces); } @Override public void setTitle(String aTitle) { - for (Task aTask : childTaskList) + for (Task aTask : childTaskL) aTask.setTitle(aTitle); } - @Override - public boolean isActive() - { - return childTaskList.get(0).isActive(); - } - } diff --git a/src/glum/task/Task.java b/src/glum/task/Task.java index 9b5fca4..6293d6c 100644 --- a/src/glum/task/Task.java +++ b/src/glum/task/Task.java @@ -1,78 +1,106 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.task; +/** + * Interface that provides support to monitor a "task". The following features are provided: + *

      + *
    • Checking the state of the task. + *
    • Querying + updating of progress. + *
    • Logging of regular messages associated with the task. + *
    • Configuration of refresh rates. + *
    + * + * @author lopeznr1 + */ public interface Task { + // Constants + /** The "invalid" {@link Task}. */ + public static final Task Invalid = InvalidTask.Instance; + /** - * Method to allow the Task to be properly aborted. After this method - * call, the method call isActive will return false. + * Method to allow the Task to be properly aborted. After this method call, the method call isActive will return + * false. */ public void abort(); - - /** - * Appends aMsg to the info buffer. The text aMsg can not be updated. - */ - public void infoAppend(String aMsg); /** - * Appends aMsg to the info buffer. The text aMsg can not be updated. - * A new line will automatically be added to the buffer after aMsg. - */ - public void infoAppendln(String aMsg); - - /** - * Updates the previous update message with the text aMsg. The text - * aMsg can be updated as long as the method infoAppend is not called. - */ - public void infoUpdate(String aMsg); - - /** - * Returns the percent of progress that has been completed. - * 0.0: Not started - * 1.0: Complete + * Returns the percent of progress that has been completed. 0.0: Not started 1.0: Complete */ public double getProgress(); - + + /** + * Returns whether this task has been aborted. + */ + public boolean isAborted(); + + /** + * Returns whether this task is still active. + */ + public boolean isActive(); + + /** + * Appends a formatted string to the regular buffer. The appended text can not be updated. + */ + public void logReg(String aFmtMsg, Object... aObjArr); + + /** + * Appends a formatted string to the regular buffer. The appended text can not be updated. A new line will + * automatically be added to the buffer after the appended text. + */ + public void logRegln(String aFmtMsg, Object... aObjArr); + + /** + * Updates the previous regular message with a formatted string. The text can be updated as long as the method + * logReg() and logRegln() are not called. + */ + public void logRegUpdate(String aFmtMsg, Object... aObjArr); + /** * Method to reset a Task to its initial state. */ public void reset(); /** - * Sets in the percent of progress that has been completed. - * 0.0: Not started - * 1.0: Complete - */ - public void setProgress(double aProgress); - - /** - * Sets it the progress of a task so that its completion - * value is a ratio of currVal to maxVal. + * Sets in the percent of progress that has been completed. 0.0: Not started 1.0: Complete */ - public void setProgress(int currVal, int maxVal); + public void setProgress(double aProgress); /** - * Sets in the maximum rate the UI will be refreshed at. + * Sets it the progress of a task so that its completion value is a ratio of aCurrVal to aMaxVal. + */ + public void setProgress(int aCurrVal, int aMaxVal); + + /** + * Sets in the maximum rate (in milliseconds) the UI will be refreshed at. */ public void setRefreshRateMs(long aRateMs); /** - * Method that sets the single line status of the task + * Method that sets the single line status of the task */ public void setStatus(String aStatus); /** * Sets in the number of spaces the tab character will be converted to */ - public void setTabSize(int numSpaces); + public void setTabSize(int aNumSpaces); /** - * Method that sets the title of the task + * Method that sets the title of the task */ public void setTitle(String aTitle); - /** - * Returns whether this task is still active - */ - public boolean isActive(); - } diff --git a/src/glum/task/TaskListener.java b/src/glum/task/TaskListener.java new file mode 100644 index 0000000..76098f3 --- /dev/null +++ b/src/glum/task/TaskListener.java @@ -0,0 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.task; + +/** + * Interface that provides for the callback mechanism for notification of updates to a {@link Task}. + * + * @author lopeznr1 + */ +public interface TaskListener +{ + /** + * Notification method that the task has been updated. + */ + public void taskUpdate(Task aTask); + +} diff --git a/src/glum/task/TaskState.java b/src/glum/task/TaskState.java deleted file mode 100644 index 39c694f..0000000 --- a/src/glum/task/TaskState.java +++ /dev/null @@ -1,20 +0,0 @@ -package glum.task; - -public enum TaskState -{ - // Initial state - Inactive, - - // Default normal state - Active, - - // Transitional states - TransAbort, - TransComplete, - TransSuspend, - - // Terminal states - Aborted, - Completed, - Suspended -} diff --git a/src/glum/text/SigFigNumberFormat.java b/src/glum/text/SigFigNumberFormat.java new file mode 100644 index 0000000..4c10eed --- /dev/null +++ b/src/glum/text/SigFigNumberFormat.java @@ -0,0 +1,115 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.text; + +import java.math.BigDecimal; +import java.math.MathContext; +import java.text.*; + +/** + * NumberFormat used to display values with a desired number of significant figures. + *

    + * TODO: This class is incomplete and was rushed just to support simple display of values with a requested number of + * significant digits. + * + * @author lopeznr1 + */ +public class SigFigNumberFormat extends NumberFormat +{ + // Attributes + private final String nanStr; + private final int numSigFigs; + + /** + * Standard Constructor + * + * @param aNumSigFigs + * The number of significant figures to display + * @param aNaNStr + * The string to show for values of NaN. + */ + public SigFigNumberFormat(int aNumSigFigs, String aNaNStr) + { + nanStr = aNaNStr; + numSigFigs = aNumSigFigs; + } + + /** + * Simplified Constructor + * + * @param aNumSigFigs + * The number of significant figures to display + */ + public SigFigNumberFormat(int aNumSigFigs) + { + this(aNumSigFigs, null); + } + + @Override + public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) + { + // Special handling for infinite + if (Double.isInfinite(number) == true) + return result.append("" + number); + + // Special handling for NaN + if (Double.isNaN(number) == true) + { + if (nanStr != null) + return result.append(nanStr); + + result.append("-."); + for (int c1 = 0; c1 < numSigFigs - 1; c1++) + result.append("-"); + + return result; + } + + BigDecimal tmpBD = getValueRoundedToSigFigs(number, numSigFigs); + return result.append("" + tmpBD); + } + + @Override + public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) + { + throw new RuntimeException("NOT IMPLEMENTED"); + } + + @Override + public Number parse(String source, ParsePosition parsePosition) + { + throw new RuntimeException("NOT IMPLEMENTED"); + } + + /** + * Utility method that returns a BigDecimal rounded to the specified number of significant figures. + *

    + * Source: https://stackoverflow.com/questions/7548841 + *

    + * TODO: Consider promoting this method to a utility class. + * + * @param aValue + * The double value to be rounded + * @param aNumSigFigs + * The number of significant figures of interest. + */ + private static BigDecimal getValueRoundedToSigFigs(double aValue, int aNumSigFigs) + { + BigDecimal retBD = new BigDecimal(aValue); + retBD = retBD.round(new MathContext(aNumSigFigs)); + + return retBD; + } + +} \ No newline at end of file diff --git a/src/glum/unit/BaseUnitProvider.java b/src/glum/unit/BaseUnitProvider.java index 67a4ddf..d7fdee5 100644 --- a/src/glum/unit/BaseUnitProvider.java +++ b/src/glum/unit/BaseUnitProvider.java @@ -1,20 +1,35 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; - /** - * Base UnitProvider class that provides the functionality of listener registration and notification. + * Base implementation of {@link UnitProvider} class that provides the functionality of listener registration and + * notification. + * + * @author lopeznr1 */ public abstract class BaseUnitProvider implements UnitProvider { - private List myListeners; + private List listenerL; private String refName; public BaseUnitProvider(String aRefName) { - myListeners = Lists.newLinkedList(); + listenerL = new ArrayList<>(); refName = aRefName; } @@ -27,13 +42,13 @@ public abstract class BaseUnitProvider implements UnitProvider @Override public void addListener(UnitListener aListener) { - myListeners.add(aListener); + listenerL.add(aListener); } @Override - public void removeListener(UnitListener aListener) + public void delListener(UnitListener aListener) { - myListeners.remove(aListener); + listenerL.remove(aListener); } /** @@ -41,7 +56,7 @@ public abstract class BaseUnitProvider implements UnitProvider */ protected void notifyListeners() { - for (UnitListener aListener : myListeners) + for (UnitListener aListener : listenerL) aListener.unitChanged(this, refName); } diff --git a/src/glum/unit/ByteUnit.java b/src/glum/unit/ByteUnit.java index cf14be4..35a9c70 100644 --- a/src/glum/unit/ByteUnit.java +++ b/src/glum/unit/ByteUnit.java @@ -1,5 +1,25 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +import java.text.DecimalFormat; + +/** + * {@link HeuristicUnit} used to display a total count of bytes. + * + * @author lopeznr1 + */ public class ByteUnit extends HeuristicUnit { // Constants @@ -8,24 +28,44 @@ public class ByteUnit extends HeuristicUnit public static final double Gigabyte = 1024 * 1024 * 1024; public static final double Terabyte = 1024 * 1024 * 1024L * 1024L; - public ByteUnit(int numDecimalPlaces) + // Attributes + private final String nanStr; + + /** Standard Constructor */ + public ByteUnit(DecimalFormat aDispFormat, String aNaNStr) { - super("Heuristic", numDecimalPlaces); + super("Heuristic", aDispFormat); + nanStr = aNaNStr; + } + + /** Simplified Constructor */ + public ByteUnit(DecimalFormat aDispFormat) + { + this(aDispFormat, "---"); + } + + /** Alternative Constructor */ + public ByteUnit(int aNumDecimalPlaces, String aNaNStr) + { + this(UnitUtil.formFormatWithNumDecimalPlaces(aNumDecimalPlaces), aNaNStr); + } + + /** Alternative Constructor */ + public ByteUnit(int aNumDecimalPlaces) + { + this(aNumDecimalPlaces, "---"); } @Override public String getString(Object aVal) { - String aStr, unitStr; - long numBytes; - double dVal; - - aStr = "N/A"; if (aVal instanceof Number == false) - return aStr; - - numBytes = ((Number)aVal).longValue(); + return nanStr; + var numBytes = ((Number) aVal).longValue(); + + String unitStr; + double dVal; if (numBytes < Kilobyte) { unitStr = "B"; @@ -52,9 +92,9 @@ public class ByteUnit extends HeuristicUnit dVal = (numBytes + 0.0) / Terabyte; } - synchronized (format) + synchronized (dispFormat) { - return format.format(dVal) + " " + unitStr; + return dispFormat.format(dVal) + " " + unitStr; } } diff --git a/src/glum/unit/ConstUnitProvider.java b/src/glum/unit/ConstUnitProvider.java index 9854494..30360ef 100644 --- a/src/glum/unit/ConstUnitProvider.java +++ b/src/glum/unit/ConstUnitProvider.java @@ -1,21 +1,37 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +import java.io.IOException; + import glum.zio.ZinStream; import glum.zio.ZoutStream; -import java.io.IOException; - /** - * UnitProvider that always returns the same Unit. + * {@link UnitProvider} that always returns the same {@link Unit}. + * + * @author lopeznr1 */ public class ConstUnitProvider implements UnitProvider { // State vars - private Unit activeUnit; + private final Unit refUnit; + /** Standard Constructor */ public ConstUnitProvider(Unit aUnit) { - activeUnit = aUnit; + refUnit = aUnit; } @Override @@ -25,17 +41,11 @@ public class ConstUnitProvider implements UnitProvider } @Override - public void removeListener(UnitListener aListener) + public void delListener(UnitListener aListener) { ; // Nothing to do } - @Override - public String getConfigName() - { - return "Const"; - } - @Override public String getDisplayName() { @@ -45,7 +55,7 @@ public class ConstUnitProvider implements UnitProvider @Override public Unit getUnit() { - return activeUnit; + return refUnit; } @Override diff --git a/src/glum/unit/Convert.java b/src/glum/unit/Convert.java index 349fc1c..9a6f24e 100644 --- a/src/glum/unit/Convert.java +++ b/src/glum/unit/Convert.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; /** Contains conversion multipliers to/from feet, yards, meters, data miles, diff --git a/src/glum/unit/DateTimeUnit.java b/src/glum/unit/DateTimeUnit.java new file mode 100644 index 0000000..239f627 --- /dev/null +++ b/src/glum/unit/DateTimeUnit.java @@ -0,0 +1,113 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.unit; + +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; + +/** + * Implementation of {@link Unit} for displaying temporal objects such as date, time, etc... + * + * @author lopeznr1 + */ +public class DateTimeUnit implements Unit +{ + // Attributes + private final String configName; + private final DateTimeFormatter formatter; + + // State vars + private String nullStr; + + /** Standard Constructor */ + public DateTimeUnit(String aConfigName, DateTimeFormatter aFormatter) + { + configName = aConfigName; + formatter = aFormatter; + + nullStr = "---"; + } + + /** Alternative Constructor */ + public DateTimeUnit(String aConfigName, String aFmtStr) + { + configName = aConfigName; + formatter = DateTimeFormatter.ofPattern(aFmtStr); + + nullStr = "---"; + } + + /** + * Sets in the string representation for null (or invalid) values + */ + public void setNaNString(String aStr) + { + nullStr = aStr; + } + + @Override + public String getConfigName() + { + return configName; + } + + @Override + public String getLabel(boolean aIsDetailed) + { + return ""; + } + + @Override + public String getString(Object aVal) + { + TemporalAccessor tmpAccessor; + + // Note we format a TemporalAccessor rather than to LocalDate,LocalTime,... + // since this is much more generic + tmpAccessor = null; + if (aVal instanceof TemporalAccessor) + tmpAccessor = (TemporalAccessor) aVal; + + // Bail if unrecognized object + if (tmpAccessor == null) + return nullStr; + + return formatter.format(tmpAccessor); + } + + @Override + public String getString(Object aVal, boolean aIsDetailed) + { + return getString(aVal); + } + + @Override + public double parseString(String aDateStr, double aErrorVal) + { + throw new UnsupportedOperationException(); + } + + @Override + public double toModel(double aVal) + { + throw new UnsupportedOperationException(); + } + + @Override + public double toUnit(double aVal) + { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/glum/unit/DateUnit.java b/src/glum/unit/DateUnit.java index a46882b..c95b975 100644 --- a/src/glum/unit/DateUnit.java +++ b/src/glum/unit/DateUnit.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; import java.text.*; @@ -102,7 +115,7 @@ public class DateUnit implements Unit } @Override - public String getLabel(boolean isDetailed) + public String getLabel(boolean aIsDetailed) { return ""; } @@ -138,7 +151,7 @@ public class DateUnit implements Unit } @Override - public String getString(Object aVal, boolean isDetailed) + public String getString(Object aVal, boolean aIsDetailed) { return getString(aVal); } diff --git a/src/glum/unit/DateUnitProvider.java b/src/glum/unit/DateUnitProvider.java index e4386a6..50e6ae8 100644 --- a/src/glum/unit/DateUnitProvider.java +++ b/src/glum/unit/DateUnitProvider.java @@ -1,16 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +import java.io.IOException; +import java.util.*; + import glum.zio.ZinStream; import glum.zio.ZoutStream; -import java.io.IOException; -import java.util.List; -import java.util.Map; -import java.util.TimeZone; - -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - +/** + * Implementation of {@link UnitProvider} that provides a unit compatible for processing chronological values. + * + * @author lopeznr1 + */ public class DateUnitProvider extends BaseUnitProvider { // Config vars @@ -20,9 +33,10 @@ public class DateUnitProvider extends BaseUnitProvider private String cfgCustomPattern; // State vars - private Map protoMap; + private Map protoM; private DateUnit activeUnit; + /** Standard Constructor */ public DateUnitProvider(String aRefName, DateUnit aActiveUnit) { super(aRefName); @@ -34,7 +48,7 @@ public class DateUnitProvider extends BaseUnitProvider cfgCustomPattern = aActiveUnit.getFormat().toPattern(); // State vars - protoMap = Maps.newHashMap(); + protoM = new HashMap<>(); activeUnit = null; // Activate the default (prototype) unit @@ -52,7 +66,7 @@ public class DateUnitProvider extends BaseUnitProvider if (cfgProtoName == null) cfgProtoName = aUnit.getConfigName(); - protoMap.put(aUnit.getConfigName(), aUnit); + protoM.put(aUnit.getConfigName(), aUnit); } /** @@ -76,7 +90,7 @@ public class DateUnitProvider extends BaseUnitProvider DateUnit protoUnit; // Ensure this proto unit is installed - protoUnit = protoMap.get(aProtoName); + protoUnit = protoM.get(aProtoName); if (protoUnit == null) throw new RuntimeException("Specified name is not installed as a prototype! aProtoName: " + aProtoName); @@ -109,7 +123,7 @@ public class DateUnitProvider extends BaseUnitProvider */ public DateUnit getProtoUnit() { - return protoMap.get(cfgProtoName); + return protoM.get(cfgProtoName); } /** @@ -117,13 +131,7 @@ public class DateUnitProvider extends BaseUnitProvider */ public List getProtoNameList() { - return Lists.newArrayList(protoMap.keySet()); - } - - @Override - public String getConfigName() - { - return activeUnit.getConfigName(); + return new ArrayList<>(protoM.keySet()); } @Override diff --git a/src/glum/unit/DecimalUnitProvider.java b/src/glum/unit/DecimalUnitProvider.java index 04e9fc4..34d2d00 100644 --- a/src/glum/unit/DecimalUnitProvider.java +++ b/src/glum/unit/DecimalUnitProvider.java @@ -1,70 +1,89 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; -import glum.zio.ZinStream; -import glum.zio.ZoutStream; - import java.io.IOException; import java.text.Format; import java.text.NumberFormat; +import java.util.ArrayList; import java.util.List; -import com.google.common.collect.Lists; +import glum.zio.ZinStream; +import glum.zio.ZoutStream; +/** + * Implementation of {@link UnitProvider} that provides a unit compatible for processing numeric values. + * + * @author lopeznr1 + */ public class DecimalUnitProvider extends BaseUnitProvider { // State vars - private List protoUnitList; + private List protoUnitL; private Unit activeUnit; - + + /** Standard Constructor */ public DecimalUnitProvider(String aRefName, Unit aActiveUnit) { super(aRefName); - - protoUnitList = Lists.newLinkedList(); + + protoUnitL = new ArrayList<>(); activeUnit = aActiveUnit; } /** * Updates the activeUnit with the specified configuration */ - public void activate(Unit protoUnit, int decimalPlaces, boolean forceFullLabel) + public void activate(Unit aProtoUnit, int aDecimalPlaces, boolean aForceFullLabel) { String formatStr; // Insanity check - if (protoUnit == null) + if (aProtoUnit == null) return; // Build the format formatStr = "###,###,###,###,###,##0"; - if (decimalPlaces > 0) + if (aDecimalPlaces > 0) { formatStr += "."; - for (int c1 = 0; c1 < decimalPlaces; c1++) + for (int c1 = 0; c1 < aDecimalPlaces; c1++) formatStr += "0"; } - if (protoUnit instanceof HeuristicUnit) - activeUnit = ((HeuristicUnit)protoUnit).spawnClone(decimalPlaces); - else if (forceFullLabel == true) - activeUnit = new NumberUnit(protoUnit.getLabel(true), protoUnit.getLabel(true), protoUnit.toUnit(1), formatStr); + if (aProtoUnit instanceof HeuristicUnit) + activeUnit = ((HeuristicUnit) aProtoUnit).spawnClone(aDecimalPlaces); + else if (aForceFullLabel == true) + activeUnit = new NumberUnit(aProtoUnit.getLabel(true), aProtoUnit.getLabel(true), aProtoUnit.toUnit(1), + formatStr); else - activeUnit = new NumberUnit(protoUnit.getLabel(true), protoUnit.getLabel(false), protoUnit.toUnit(1), formatStr); - + activeUnit = new NumberUnit(aProtoUnit.getLabel(true), aProtoUnit.getLabel(false), aProtoUnit.toUnit(1), + formatStr); + notifyListeners(); } /** - * Adds in the specified Unit as a prototype (which can be used to configure - * the active unit) + * Adds in the specified Unit as a prototype (which can be used to configure the active unit) */ public void addProtoUnit(Unit aProtoUnit) { // Insanity check if (aProtoUnit instanceof HeuristicUnit == false && aProtoUnit instanceof NumberUnit == false) throw new RuntimeException("ProtoUnit must either be of type HeuristicUnit or NumberUnit"); - - protoUnitList.add(aProtoUnit); + + protoUnitL.add(aProtoUnit); } /** @@ -76,7 +95,7 @@ public class DecimalUnitProvider extends BaseUnitProvider aFormat = activeUnit.getFormat(); if (aFormat instanceof NumberFormat) - return ((NumberFormat)aFormat).getMaximumFractionDigits(); + return ((NumberFormat) aFormat).getMaximumFractionDigits(); return 0; } @@ -87,10 +106,10 @@ public class DecimalUnitProvider extends BaseUnitProvider public boolean getForceFullLabel() { String fullLabel, shortLabel; - + if (activeUnit instanceof HeuristicUnit) return false; - + fullLabel = activeUnit.getLabel(true); shortLabel = activeUnit.getLabel(false); return fullLabel.equals(shortLabel); @@ -102,11 +121,11 @@ public class DecimalUnitProvider extends BaseUnitProvider public Unit getProtoUnit() { String activeLabel, protoLabel; - + activeLabel = activeUnit.getLabel(true); - for (Unit aUnit : protoUnitList) + for (Unit aUnit : protoUnitL) { - protoLabel = aUnit.getLabel(true); + protoLabel = aUnit.getLabel(true); if (protoLabel.equals(activeLabel) == true) return aUnit; } @@ -119,13 +138,7 @@ public class DecimalUnitProvider extends BaseUnitProvider */ public List getProtoUnitList() { - return Lists.newArrayList(protoUnitList); - } - - @Override - public String getConfigName() - { - return activeUnit.getConfigName(); + return new ArrayList<>(protoUnitL); } @Override @@ -141,18 +154,18 @@ public class DecimalUnitProvider extends BaseUnitProvider int protoUnitIdx; int decimalPlaces; boolean forceFullLabel; - + // Read the stream's content aStream.readVersion(0); - + protoUnitIdx = aStream.readInt(); decimalPlaces = aStream.readInt(); forceFullLabel = aStream.readBool(); - + // Install the configuration protoUnit = null; - if (protoUnitIdx >=0 && protoUnitIdx < protoUnitList.size()) - protoUnit = protoUnitList.get(protoUnitIdx); + if (protoUnitIdx >= 0 && protoUnitIdx < protoUnitL.size()) + protoUnit = protoUnitL.get(protoUnitIdx); activate(protoUnit, decimalPlaces, forceFullLabel); } @@ -162,15 +175,15 @@ public class DecimalUnitProvider extends BaseUnitProvider int protoUnitIdx; int decimalPlaces; boolean forceFulLabel; - + // Retrieve the configuration - protoUnitIdx = protoUnitList.indexOf(getProtoUnit()); + protoUnitIdx = protoUnitL.indexOf(getProtoUnit()); decimalPlaces = getDecimalPlaces(); forceFulLabel = getForceFullLabel(); // Write the stream's contents aStream.writeVersion(0); - + aStream.writeInt(protoUnitIdx); aStream.writeInt(decimalPlaces); aStream.writeBool(forceFulLabel); diff --git a/src/glum/unit/EmptyUnitProvider.java b/src/glum/unit/EmptyUnitProvider.java index a32586b..e19bab2 100644 --- a/src/glum/unit/EmptyUnitProvider.java +++ b/src/glum/unit/EmptyUnitProvider.java @@ -1,10 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +import java.io.IOException; + import glum.zio.ZinStream; import glum.zio.ZoutStream; -import java.io.IOException; - +/** + * {@link UnitProvider} that always returns null for the {@link Unit}. + * + * @author lopeznr1 + */ public class EmptyUnitProvider implements UnitProvider { @Override @@ -14,17 +32,11 @@ public class EmptyUnitProvider implements UnitProvider } @Override - public void removeListener(UnitListener aListener) + public void delListener(UnitListener aListener) { ; // Nothing to do } - @Override - public String getConfigName() - { - return "None"; - } - @Override public String getDisplayName() { diff --git a/src/glum/unit/HeuristicUnit.java b/src/glum/unit/HeuristicUnit.java index bb2ae28..2e560df 100644 --- a/src/glum/unit/HeuristicUnit.java +++ b/src/glum/unit/HeuristicUnit.java @@ -1,41 +1,48 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; import java.text.DecimalFormat; import java.text.Format; /** - * A special kind of Unit that converts the value to the most human readable format. Thus, this Unit does not support - * any internal model, and calling associated model conversion routines will throw an - * {@link UnsupportedOperationException}.
    - *
    + * Implementation of {@link Unit} that converts the value to the most human readable format. + *

    + * This Unit does not support any internal model, and calling associated model conversion routines will throw an + * {@link UnsupportedOperationException}. + *

    * The primary method to override is getString(Object aVal) + * + * @author lopeznr1 */ public abstract class HeuristicUnit implements Unit { - // State vars - private String configName; - protected DecimalFormat format; + // Attributes + private final String configName; + protected final DecimalFormat dispFormat; - public HeuristicUnit(String aConfigName, int numDecimalPlaces) + /** Standard Constructor */ + public HeuristicUnit(String aConfigName, DecimalFormat aDispFormat) { - String aStr; - configName = aConfigName; - - aStr = "#0"; - if (numDecimalPlaces > 0) - { - aStr = "#0."; - for (int c1 = 0; c1 < numDecimalPlaces; c1++) - aStr += "0"; - } - format = new DecimalFormat(aStr); + dispFormat = aDispFormat; } /** * Spawns a near exact copy of this HeuristicUnit (only different number of decimal places) */ - public abstract HeuristicUnit spawnClone(int numDecimalPlaces); + public abstract HeuristicUnit spawnClone(int aNumDecimalPlaces); @Override public String getConfigName() @@ -46,20 +53,20 @@ public abstract class HeuristicUnit implements Unit @Override public Format getFormat() { - if (format == null) + if (dispFormat == null) return null; - return (Format)format.clone(); + return (Format) dispFormat.clone(); } @Override - public String getLabel(boolean isDetailed) + public String getLabel(boolean aIsDetailed) { return ""; } @Override - public String getString(Object aVal, boolean isDetailed) + public String getString(Object aVal, boolean aIsDetailed) { return getString(aVal); } diff --git a/src/glum/unit/LatLonUnitProvider.java b/src/glum/unit/LatLonUnitProvider.java deleted file mode 100644 index 1ebfa3a..0000000 --- a/src/glum/unit/LatLonUnitProvider.java +++ /dev/null @@ -1,173 +0,0 @@ -package glum.unit; - -import glum.zio.ZinStream; -import glum.zio.ZoutStream; - -import java.io.IOException; -import java.text.Format; -import java.text.NumberFormat; - -public class LatLonUnitProvider extends BaseUnitProvider -{ - // State vars - private Unit latUnit, lonUnit; - - public LatLonUnitProvider(String aRefName) - { - super(aRefName); - - activateStandard(false); - } - - /** - * Updates the active lat,lon unit to be configured in raw format - */ - public void activateRaw(int aDecimalPlaces, boolean aIsZeroCentered) - { - // Set in the unit - if (aIsZeroCentered == true) - { - latUnit = new ShiftedUnit("", "", 0, aDecimalPlaces); - lonUnit = new ShiftedUnit("", "", 0, aDecimalPlaces); - } - else - { - latUnit = new ShiftedUnit("", "", 90, aDecimalPlaces); - lonUnit = new ShiftedUnit("", "", 180, aDecimalPlaces); - } - - notifyListeners(); - } - - /** - * Updates the active lat,lon unit to be configured in standard format - */ - public void activateStandard(boolean aIsSecondsShown) - { - latUnit = new LatUnit(aIsSecondsShown); - lonUnit = new LonUnit(aIsSecondsShown); - notifyListeners(); - } - - /** - * Returns the number of decimal places used in the raw unit. - */ - public int getDecimalPlaces() - { - Format aFormat; - - aFormat = latUnit.getFormat(); - if (aFormat instanceof NumberFormat) - return ((NumberFormat)aFormat).getMaximumFractionDigits(); - - return 0; - } - - /** - * Returns the active lat unit - */ - public Unit getLatUnit() - { - return latUnit; - } - - /** - * Returns the active lon unit - */ - public Unit getLonUnit() - { - return lonUnit; - } - - /** - * Returns whether the raw unit is zero centered. This is true only for properly configured active raw units. - */ - public boolean isZeroCentered() - { - if (latUnit instanceof LatUnit) - return false; - - return (latUnit.toModel(0) == 0); - } - - /** - * Returns whether the active lat,lon unit is set to display in the raw format - */ - public boolean isRawUnitActive() - { - if (latUnit instanceof LatUnit) - return false; - - return true; - } - - /** - * Returns whether the active lat,lon unit will display seconds. This is true only for properly configured active - * standard units. - */ - public boolean isSecondsShown() - { - if (latUnit instanceof LatUnit) - return ((LatUnit)latUnit).isSecondsShown(); - - return false; - } - - @Override - public String getConfigName() - { - if (latUnit instanceof LatUnit) - return "Standard"; - - return "Raw"; - } - - @Override - public Unit getUnit() - { - throw new RuntimeException("Please use getLatUnit() or getLonUnit() instead."); - } - - @Override - public void zioRead(ZinStream aStream) throws IOException - { - int decimalPlaces; - boolean isRawUnit, isZeroCentered, isSecondsShown; - - // Read the stream's content - aStream.readVersion(0); - - isRawUnit = aStream.readBool(); - decimalPlaces = aStream.readInt(); - isZeroCentered = aStream.readBool(); - isSecondsShown = aStream.readBool(); - - // Install the configuration - if (isRawUnit == true) - activateRaw(decimalPlaces, isZeroCentered); - else - activateStandard(isSecondsShown); - } - - @Override - public void zioWrite(ZoutStream aStream) throws IOException - { - int decimalPlaces; - boolean isRawUnit, isZeroCentered, isSecondsShown; - - // Retrieve the configuration - isRawUnit = isRawUnitActive(); - decimalPlaces = getDecimalPlaces(); - isZeroCentered = isZeroCentered(); - isSecondsShown = isSecondsShown(); - - // Write the stream's contents - aStream.writeVersion(0); - - aStream.writeBool(isRawUnit); - aStream.writeInt(decimalPlaces); - aStream.writeBool(isZeroCentered); - aStream.writeBool(isSecondsShown); - } - -} diff --git a/src/glum/unit/LatUnit.java b/src/glum/unit/LatUnit.java deleted file mode 100644 index 6228e79..0000000 --- a/src/glum/unit/LatUnit.java +++ /dev/null @@ -1,94 +0,0 @@ -package glum.unit; - -import java.text.*; - -import glum.coord.*; - -public class LatUnit implements Unit -{ - // State vars - private boolean isSecondsShown; - - /** - * Constructor - */ - public LatUnit(boolean aIsSecondsShown) - { - isSecondsShown = aIsSecondsShown; - } - - /** - * isSecondsShown - */ - public boolean isSecondsShown() - { - return isSecondsShown; - } - - @Override - public String getConfigName() - { - return "Lat"; - } - - @Override - public Format getFormat() - { - return null; - } - - @Override - public String getLabel(boolean isDetailed) - { - return ""; - } - - @Override - public String getString(Object aObj) - { - Double aVal; - - if (aObj instanceof Number) - aVal = ((Number)aObj).doubleValue(); -/* else if (aObj instanceof LatLon) - aVal = ((LatLon)aVal).lat; -*/ else - return "N/A"; - - return CoordUtil.LatToString(aVal, isSecondsShown); - } - - @Override - public String getString(Object aObj, boolean isDetailed) - { - Double aVal; - - if (aObj instanceof Number) - aVal = ((Number)aObj).doubleValue(); -/* else if (aObj instanceof LatLon) - aVal = ((LatLon)aVal).lat; -*/ else - return "N/A"; - - return CoordUtil.LatToString(aVal, isSecondsShown); - } - - @Override - public double parseString(String aStr, double eVal) - { - return CoordUtil.StringToLat(aStr); - } - - @Override - public double toModel(double aVal) - { - return aVal; - } - - @Override - public double toUnit(double aVal) - { - return aVal; - } - -} diff --git a/src/glum/unit/LonUnit.java b/src/glum/unit/LonUnit.java deleted file mode 100644 index eb1caca..0000000 --- a/src/glum/unit/LonUnit.java +++ /dev/null @@ -1,94 +0,0 @@ -package glum.unit; - -import java.text.*; - -import glum.coord.*; - -public class LonUnit implements Unit -{ - // State vars - private boolean isSecondsShown; - - /** - * Constructor - */ - public LonUnit(boolean aIsSecondsShown) - { - isSecondsShown = aIsSecondsShown; - } - - /** - * isSecondsShown - */ - public boolean isSecondsShown() - { - return isSecondsShown; - } - - @Override - public String getConfigName() - { - return "Lon"; - } - - @Override - public Format getFormat() - { - return null; - } - - @Override - public String getLabel(boolean isDetailed) - { - return ""; - } - - @Override - public String getString(Object aObj) - { - Double aVal; - - if (aObj instanceof Number) - aVal = ((Number)aObj).doubleValue(); -/* else if (aObj instanceof LatLon) - aVal = ((LatLon)aVal).lon; -*/ else - return "N/A"; - - return CoordUtil.LonToString(aVal, isSecondsShown); - } - - @Override - public String getString(Object aObj, boolean isDetailed) - { - Double aVal; - - if (aObj instanceof Number) - aVal = ((Number)aObj).doubleValue(); -/* else if (aObj instanceof LatLon) - aVal = ((LatLon)aVal).lon; -*/ else - return "N/A"; - - return CoordUtil.LonToString(aVal, isSecondsShown); - } - - @Override - public double parseString(String aStr, double eVal) - { - return CoordUtil.StringToLon(aStr); - } - - @Override - public double toModel(double aVal) - { - return aVal; - } - - @Override - public double toUnit(double aVal) - { - return aVal; - } - -} diff --git a/src/glum/unit/NumberInverseUnit.java b/src/glum/unit/NumberInverseUnit.java index 7235eb8..e492e3b 100644 --- a/src/glum/unit/NumberInverseUnit.java +++ b/src/glum/unit/NumberInverseUnit.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; import java.text.DecimalFormat;; @@ -28,17 +41,17 @@ public class NumberInverseUnit extends NumberUnit if (aVal instanceof Number == false) return aStr; - if (format == null) + if (dispFormat == null) return "" + ((1.0/((Number)aVal).doubleValue()) * conversionFactor); - synchronized (format) + synchronized (dispFormat) { - return format.format((1.0/((Number)aVal).doubleValue()) * conversionFactor); + return dispFormat.format((1.0/((Number)aVal).doubleValue()) * conversionFactor); } } @Override - public String getString(Object aVal, boolean isDetailed) + public String getString(Object aVal, boolean aIsDetailed) { if (aVal instanceof Number == false) return "N/A"; diff --git a/src/glum/unit/NumberUnit.java b/src/glum/unit/NumberUnit.java index 3c00119..b345e76 100644 --- a/src/glum/unit/NumberUnit.java +++ b/src/glum/unit/NumberUnit.java @@ -1,57 +1,60 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; -import glum.gui.GuiUtil; +import java.text.DecimalFormat; +import java.text.Format; -import java.text.*; +import glum.io.ParseUtil; +/** + * Implementation of {@link Unit} for displaying decimal values. + * + * @author lopeznr1 + */ public class NumberUnit implements Unit { // State vars - protected DecimalFormat format; + protected DecimalFormat dispFormat; protected String nanStr; protected String fullLabel; protected String shortLabel; protected double conversionFactor; - /** - * Constructor - */ + /** Standard Constructor */ + public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, DecimalFormat aDispFormat) + { + nanStr = "---"; + fullLabel = aFullLabel; + shortLabel = aShortLabel; + conversionFactor = aConversionFactor; + dispFormat = aDispFormat; + } + + public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, String aFormatStr) + { + this(aFullLabel, aShortLabel, aConversionFactor, new DecimalFormat(aFormatStr)); + } + public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor) { - this(aFullLabel, aShortLabel, aConversionFactor, (DecimalFormat)null); + this(aFullLabel, aShortLabel, aConversionFactor, (DecimalFormat) null); } - public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, String aDecimalFormatStr) + public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, int aNumDecimalPlaces) { - this(aFullLabel, aShortLabel, aConversionFactor, new DecimalFormat(aDecimalFormatStr)); - } - - public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, DecimalFormat aFormat) - { - nanStr = "---"; - fullLabel = aFullLabel; - shortLabel = aShortLabel; - conversionFactor = aConversionFactor; - format = aFormat; - } - - public NumberUnit(String aFullLabel, String aShortLabel, double aConversionFactor, int numDecimalPlaces) - { - String aStr; - - nanStr = "---"; - fullLabel = aFullLabel; - shortLabel = aShortLabel; - conversionFactor = aConversionFactor; - - aStr = "#0"; - if (numDecimalPlaces > 0) - { - aStr = "#0."; - for (int c1 = 0; c1 < numDecimalPlaces; c1++) - aStr += "0"; - } - format = new DecimalFormat(aStr); + this(aFullLabel, aShortLabel, aConversionFactor, UnitUtil.formFormatWithNumDecimalPlaces(aNumDecimalPlaces)); } /** @@ -59,12 +62,12 @@ public class NumberUnit implements Unit */ public boolean isFloating() { - if (format != null && format.getMaximumFractionDigits() == 0) + if (dispFormat != null && dispFormat.getMaximumFractionDigits() == 0) return false; return true; -//System.out.println("NumFracDigits:" + format.getMaximumFractionDigits()); +//System.out.println("NumFracDigits:" + format.getMaximumFractionDigits()); // return format.isParseIntegerOnly(); } @@ -79,10 +82,10 @@ public class NumberUnit implements Unit @Override public Format getFormat() { - if (format == null) + if (dispFormat == null) return null; - return (Format)format.clone(); + return (Format) dispFormat.clone(); } @Override @@ -92,9 +95,9 @@ public class NumberUnit implements Unit } @Override - public String getLabel(boolean isDetailed) + public String getLabel(boolean aIsDetailed) { - if (isDetailed == true) + if (aIsDetailed == true) return fullLabel; else return shortLabel; @@ -106,27 +109,27 @@ public class NumberUnit implements Unit if (aVal instanceof Number == false) return nanStr; - double doubleVal = ((Number)aVal).doubleValue(); + double doubleVal = ((Number) aVal).doubleValue(); if (Double.isNaN(doubleVal) == true) return nanStr; - if (format == null) + if (dispFormat == null) return "" + doubleVal * conversionFactor; - synchronized (format) + synchronized (dispFormat) { - return format.format(doubleVal * conversionFactor); + return dispFormat.format(doubleVal * conversionFactor); } } @Override - public String getString(Object aVal, boolean isDetailed) + public String getString(Object aVal, boolean aIsDetailed) { // Delegate String retStr = getString(aVal); // Add the label component - if (isDetailed == true) + if (aIsDetailed == true) retStr += " " + fullLabel; else retStr += " " + shortLabel; @@ -139,7 +142,7 @@ public class NumberUnit implements Unit { double aVal; - aVal = GuiUtil.readDouble(aStr, Double.NaN); + aVal = ParseUtil.readDouble(aStr, Double.NaN); return toModel(aVal); } diff --git a/src/glum/unit/ShiftedUnit.java b/src/glum/unit/ShiftedUnit.java index e06aa3d..0a9cea2 100644 --- a/src/glum/unit/ShiftedUnit.java +++ b/src/glum/unit/ShiftedUnit.java @@ -1,7 +1,25 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; -import java.text.*; +import java.text.Format; +/** + * Implementation of {@link Unit} for displaying values shifted by a constant value. + * + * @author lopeznr1 + */ public class ShiftedUnit implements Unit { // State vars @@ -11,9 +29,7 @@ public class ShiftedUnit implements Unit private String shortLabel; private double deltaValue; - /** - * Constructor - */ + /** Standard Constructor */ public ShiftedUnit(String aFullLabel, String aShortLabel, double aDeltaValue, Format aFormat) { nanStr = "---"; @@ -22,23 +38,9 @@ public class ShiftedUnit implements Unit deltaValue = aDeltaValue; format = aFormat; } - public ShiftedUnit(String aFullLabel, String aShortLabel, double aDeltaValue, int numDecimalPlaces) + public ShiftedUnit(String aFullLabel, String aShortLabel, double aDeltaValue, int aNumDecimalPlaces) { - String aStr; - - aStr = "#0"; - if (numDecimalPlaces > 0) - { - aStr = "#0."; - for (int c1 = 0; c1 < numDecimalPlaces; c1++) - aStr += "0"; - } - - nanStr = "---"; - fullLabel = aFullLabel; - shortLabel = aShortLabel; - deltaValue = aDeltaValue; - format = new DecimalFormat(aStr); + this(aFullLabel, aShortLabel, aDeltaValue, UnitUtil.formFormatWithNumDecimalPlaces(aNumDecimalPlaces)); } @Override @@ -57,9 +59,9 @@ public class ShiftedUnit implements Unit } @Override - public String getLabel(boolean isDetailed) + public String getLabel(boolean aIsDetailed) { - if (isDetailed == true) + if (aIsDetailed == true) return fullLabel; else return shortLabel; @@ -88,7 +90,7 @@ public class ShiftedUnit implements Unit } @Override - public String getString(Object aObj, boolean isDetailed) + public String getString(Object aObj, boolean aIsDetailed) { double aVal; @@ -102,7 +104,7 @@ public class ShiftedUnit implements Unit if (format == null) { - if (isDetailed == true) + if (aIsDetailed == true) return "" + (aVal + deltaValue) + " " + fullLabel; else return "" + (aVal + deltaValue) + " " + shortLabel; @@ -110,7 +112,7 @@ public class ShiftedUnit implements Unit synchronized (format) { - if (isDetailed == true) + if (aIsDetailed == true) return format.format(Double.valueOf(aVal + deltaValue)) + " " + fullLabel; else return format.format(Double.valueOf(aVal + deltaValue)) + " " + shortLabel; diff --git a/src/glum/unit/TimeCountUnit.java b/src/glum/unit/TimeCountUnit.java index d84c573..78aacc9 100644 --- a/src/glum/unit/TimeCountUnit.java +++ b/src/glum/unit/TimeCountUnit.java @@ -1,12 +1,39 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; -import static glum.util.TimeConst.*; +import static glum.util.TimeConst.MS_IN_DAY; +import static glum.util.TimeConst.MS_IN_HOUR; +import static glum.util.TimeConst.MS_IN_MIN; +import static glum.util.TimeConst.MS_IN_SEC; + +import java.text.DecimalFormat; +import java.time.Duration; + import glum.util.WallTimer; /** - * Unit used to display a total count of time. This Unit is not configurable and only numerical values should be passed - * in where each increment represents 1 millisecond. If a WallTimer is passed in then this Unit will display it's total - * time. + * {@link HeuristicUnit} used to display a total count of time. + *

    + * This unit supports three types of inputs: + *

      + *
    • Numerical values - where each increment represents 1 millisecond. + *
    • {@link WallTimer} - where this unit will display it's total time. + *
    • {@link Duration} + *
    + * + * @author lopeznr1 */ public class TimeCountUnit extends HeuristicUnit { @@ -15,43 +42,53 @@ public class TimeCountUnit extends HeuristicUnit private static final long MAX_SEC_BEFORE_FMT_HOUR = 2 * MS_IN_HOUR; private static final long MAX_SEC_BEFORE_FMT_MIN = 2 * MS_IN_MIN; - // State vars - private String nanStr; + // Attributes + private final String nanStr; - public TimeCountUnit(int numDecimalPlaces) + /** Standard Constructor */ + public TimeCountUnit(DecimalFormat aDispFormat, String aNanStr) { - super("Heuristic", numDecimalPlaces); - nanStr = "---"; + super("Heuristic", aDispFormat); + nanStr = aNanStr; + } + + /** Alternative Constructor */ + public TimeCountUnit(int aNumDecimalPlaces, String aNanStr) + { + this(UnitUtil.formFormatWithNumDecimalPlaces(aNumDecimalPlaces), aNanStr); + } + + /** Simplified Constructor */ + public TimeCountUnit(int aNumDecimalPlaces) + { + this(UnitUtil.formFormatWithNumDecimalPlaces(aNumDecimalPlaces), "---"); } @Override public String getString(Object aObj) { - double numMS; - String aStr; - // Transform WallTimers to their total count if (aObj instanceof WallTimer) - aObj = ((WallTimer)aObj).getTotal(); + aObj = ((WallTimer) aObj).getTotal(); // We need a number - if (aObj instanceof Number == false) - return "N/A"; + if (aObj instanceof Duration) + aObj = ((Duration) aObj).toMillis(); + else if (aObj instanceof Number == false) + return nanStr; - numMS = ((Number)aObj).doubleValue(); + var numMS = ((Number) aObj).doubleValue(); if (Double.isNaN(numMS) == true) return nanStr; if (numMS > MAX_SEC_BEFORE_FMT_DAY) - aStr = format.format(numMS / MS_IN_DAY) + " days"; + return dispFormat.format(numMS / MS_IN_DAY) + " days"; else if (numMS > MAX_SEC_BEFORE_FMT_HOUR) - aStr = format.format(numMS / MS_IN_HOUR) + " hrs"; + return dispFormat.format(numMS / MS_IN_HOUR) + " hrs"; else if (numMS > MAX_SEC_BEFORE_FMT_MIN) - aStr = format.format(numMS / MS_IN_MIN) + " min"; + return dispFormat.format(numMS / MS_IN_MIN) + " min"; else - aStr = format.format(numMS / MS_IN_SEC) + " sec"; - - return aStr; + return dispFormat.format(numMS / MS_IN_SEC) + " sec"; } @Override diff --git a/src/glum/unit/Unit.java b/src/glum/unit/Unit.java index 793474a..d546a42 100644 --- a/src/glum/unit/Unit.java +++ b/src/glum/unit/Unit.java @@ -1,7 +1,28 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; -import java.text.*; +import java.text.Format; +/** + * Interface that provides a mechanism of transforming model values to a user oriented string. + *

    + * Transforming a user oriented string back to a (numerical) model value is supported via + * {@link #parseString(String, double)}. + * + * @author lopeznr1 + */ public interface Unit { /** @@ -12,29 +33,29 @@ public interface Unit /** * Returns Format object associated with the unit. */ - public Format getFormat(); + public default Format getFormat() + { + return null; + } /** * Returns the label associated with the unit. */ - public String getLabel(boolean isDetailed); - + public String getLabel(boolean aIsDetailed); + /** - * Returns string representation of aVal w.r.t this unit without the - * associated label. + * Returns string representation of aVal w.r.t this unit without the associated label. */ public String getString(Object aVal); /** - * Returns string representation of aVal w.r.t this unit with the associated - * (detailed if isDetailed == true) label. + * Returns string representation of aVal w.r.t this unit with the associated (detailed if isDetailed == true) label. */ - public String getString(Object aVal, boolean isDetailed); + public String getString(Object aVal, boolean aIsDetailed); /** - * Returns the model value which corresponds to the specified input string. - * The input string should be in this unit. If no value can be parsed then - * eVal is returned. + * Returns the model value which corresponds to the specified input string. The input string should be in this unit. + * If no value can be parsed then eVal is returned. */ public double parseString(String aStr, double eVal); @@ -44,8 +65,7 @@ public interface Unit public double toModel(double aVal); /** - * Returns aVal in terms of this unit. Note aVal is assumed to be in model - * units. + * Returns aVal in terms of this unit. Note aVal is assumed to be in model units. */ public double toUnit(double aVal); diff --git a/src/glum/unit/UnitListener.java b/src/glum/unit/UnitListener.java index 48d6986..f781b75 100644 --- a/src/glum/unit/UnitListener.java +++ b/src/glum/unit/UnitListener.java @@ -1,9 +1,27 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; +/** + * Interface that provides for the callback mechanism for notification of unit changes. + * + * @author lopeznr1 + */ public interface UnitListener { /** - * Event callback for notification of whenever a Unit changes in aManager + * Notification method that the unit has changed. */ public void unitChanged(UnitProvider aProvider, String aKey); diff --git a/src/glum/unit/UnitProvider.java b/src/glum/unit/UnitProvider.java index be070d6..db2700a 100644 --- a/src/glum/unit/UnitProvider.java +++ b/src/glum/unit/UnitProvider.java @@ -1,23 +1,37 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.unit; import glum.zio.ZioObj; +/** + * Interface that provides a mechanism for accessing a {@link Unit} and getting notification when that {@link Unit} has + * changed. + * + * @author lopeznr1 + */ public interface UnitProvider extends ZioObj { /** * Adds a Listener for Unit changes */ public void addListener(UnitListener aListener); - + /** * Removes a Listener for Unit changes */ - public void removeListener(UnitListener aListener); - - /** - * Returns the name of the current configuration - */ - public String getConfigName(); + public void delListener(UnitListener aListener); /** * Returns the official name name associated with the Unit @@ -25,7 +39,7 @@ public interface UnitProvider extends ZioObj public String getDisplayName(); /** - * Returns the Unit associated with this provider + * Returns the {@link Unit} associated with this provider */ public Unit getUnit(); diff --git a/src/glum/unit/UnitUtil.java b/src/glum/unit/UnitUtil.java new file mode 100644 index 0000000..b7faba4 --- /dev/null +++ b/src/glum/unit/UnitUtil.java @@ -0,0 +1,43 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.unit; + +import java.text.DecimalFormat; + +/** + * Collection of utility methods to support working with {@link Unit}s. + * + * @author lopeznr1 + */ +public class UnitUtil +{ + /** + * Utility method to create a {@link DecimalFormat} that has the specified number of decimal places + */ + public static DecimalFormat formFormatWithNumDecimalPlaces(int aNumDecimalPlaces) + { + String tmpStr; + + tmpStr = "#0"; + if (aNumDecimalPlaces > 0) + { + tmpStr = "#0."; + for (int c1 = 0; c1 < aNumDecimalPlaces; c1++) + tmpStr += "0"; + } + + return new DecimalFormat(tmpStr); + } + +} diff --git a/src/glum/util/DateUtil.java b/src/glum/util/DateUtil.java index abed8c9..422157b 100644 --- a/src/glum/util/DateUtil.java +++ b/src/glum/util/DateUtil.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; import java.util.*; @@ -9,7 +22,7 @@ public class DateUtil /** * Returns the date as a double that best describes the currDate position relative to startDate and endDate. - *

    + *

    * Return should be between 0 - 1. */ public static double computeFractionalPosition(Calendar startDate, Calendar currDate, Calendar endDate) diff --git a/src/glum/util/ImageUtil.java b/src/glum/util/ImageUtil.java index c0c580f..bd722aa 100644 --- a/src/glum/util/ImageUtil.java +++ b/src/glum/util/ImageUtil.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; import java.awt.image.BufferedImage; diff --git a/src/glum/util/MathUtil.java b/src/glum/util/MathUtil.java index 67d865b..814c3c0 100644 --- a/src/glum/util/MathUtil.java +++ b/src/glum/util/MathUtil.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; import java.awt.Dimension; diff --git a/src/glum/util/ThreadUtil.java b/src/glum/util/ThreadUtil.java index 0216f11..f2a01ab 100644 --- a/src/glum/util/ThreadUtil.java +++ b/src/glum/util/ThreadUtil.java @@ -1,15 +1,80 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; -import glum.task.Task; - import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; import javax.swing.SwingUtilities; +import glum.task.Task; + +/** + * Collection of thread utility methods. + *

    + * The provided utility methods fall under the following classes: + *

      + *
    • Transforming a stack trace into a string. + *
    • Scheduling a {@link Runnable} on a separate thread, the AWT, or as a shutdown hook. + *
    • Sleeping / waiting on a thread. + *
    + * + * @author lopeznr1 + */ public class ThreadUtil { + /** + * Utility method to register the runnable into the JVM's shutdown hook. + */ + public static void addShutdownHook(Runnable aRunnable) + { + Thread tmpThread = new Thread(aRunnable); + Runtime.getRuntime().addShutdownHook(tmpThread); + } + + /** + * Utility method to print the stack trace of aExp to a string + */ + public static String getStackTrace(Throwable aThrowable) + { + StringBuilder tmpSB = new StringBuilder(); + tmpSB.append(aThrowable.getClass().getName() + "\n"); + tmpSB.append("Msg: " + aThrowable.getMessage() + "\n"); + + // Print out the stack trace + for (StackTraceElement aItem : aThrowable.getStackTrace()) + tmpSB.append(" " + aItem.toString() + "\n"); + + // Print out any cause + // TODO + + return tmpSB.toString(); + } + + /** + * Utility method to print the stack trace of aExp to a string exactly as {@link Throwable#printStackTrace} + */ + public static String getStackTraceClassic(Throwable aThrowable) + { + StringWriter tmpSW = new StringWriter(); + PrintWriter tmpPW = new PrintWriter(tmpSW); + aThrowable.printStackTrace(tmpPW); + + tmpPW.close(); + return tmpSW.toString(); + } /** * Utility method to execute aRunnable synchronously on the AWT event dispatching thread with out throwing an @@ -25,11 +90,11 @@ public class ThreadUtil SwingUtilities.invokeAndWait(aRunnable); break; } - catch(InterruptedException aExp) + catch (InterruptedException aExp) { aExp.printStackTrace(); } - catch(InvocationTargetException aExp) + catch (InvocationTargetException aExp) { // This is an unrecoverable exception throw new RuntimeException(aExp); @@ -42,29 +107,25 @@ public class ThreadUtil */ public static void launchRunnable(Runnable aRunnable, String threadName) { - Thread aThread; - - aThread = new Thread(aRunnable, threadName); - aThread.start(); + Thread tmpThread = new Thread(aRunnable, threadName); + tmpThread.start(); } /** * Utility method to suspend the current thread for numMS milliseconds. - * + * * @param aTask - * This method will return prematurely if aTask is no longer active. + * This method will return prematurely if aTask is no longer active. */ public static void safeSleep(long numMS, Task aTask) { - long wakeTime; - - wakeTime = System.currentTimeMillis() + numMS; + long wakeTime = System.currentTimeMillis() + numMS; sleepUntilTime(wakeTime, aTask); } /** * Utility method to sleep for numMS milliseconds without throwing an {@link InterruptedException}. - * + * * @return True: if the thread was interrupted */ public static boolean safeSleep(long numMS) @@ -73,18 +134,18 @@ public class ThreadUtil { Thread.sleep(numMS); } - catch(InterruptedException aExp) + catch (InterruptedException aExp) { return true; } return false; } - + /** * Utility method to wait for a signal without throwing an {@link InterruptedException}. - * + * * @see Thread#wait() - * + * * @return True: if the thread was interrupted */ public static boolean safeWait(Object aLock) @@ -93,7 +154,7 @@ public class ThreadUtil { aLock.wait(); } - catch(InterruptedException aExp) + catch (InterruptedException aExp) { return true; } @@ -102,9 +163,9 @@ public class ThreadUtil /** * Utility method to wait for a signal without throwing an {@link InterruptedException}. - * + * * @see Thread#wait(long) - * + * * @return True: if the thread was interrupted */ public static boolean safeWait(Object aLock, long aMaxTimeMS) @@ -113,7 +174,7 @@ public class ThreadUtil { aLock.wait(aMaxTimeMS); } - catch(InterruptedException aExp) + catch (InterruptedException aExp) { return true; } @@ -122,20 +183,17 @@ public class ThreadUtil /** * Utility method to suspend the current thread until the system time is at or has passed nextWakeTime - * + * * @return True: if the thread was interrupted */ public static boolean sleepUntilTime(long nextWakeTime) { - long currTime, sleepTime; - boolean isInterrupt; - - currTime = System.currentTimeMillis(); + long currTime = System.currentTimeMillis(); while (currTime < nextWakeTime) { - sleepTime = nextWakeTime - currTime; + long sleepTime = nextWakeTime - currTime; - isInterrupt = ThreadUtil.safeSleep(sleepTime); + boolean isInterrupt = ThreadUtil.safeSleep(sleepTime); if (isInterrupt == true) return true; @@ -147,59 +205,20 @@ public class ThreadUtil /** * Utility method to suspend the current thread until the system time is at or has passed nextWakeTime - * + * * @param aTask - * This method will return prematurely if aTask is no longer active. + * This method will return prematurely if aTask is no longer active. */ public static void sleepUntilTime(long nextWakeTime, Task aTask) { - long currTime, sleepTime; - - currTime = System.currentTimeMillis(); + long currTime = System.currentTimeMillis(); while (currTime < nextWakeTime && aTask.isActive() == true) { - sleepTime = nextWakeTime - currTime; + long sleepTime = nextWakeTime - currTime; ThreadUtil.safeSleep(sleepTime); currTime = System.currentTimeMillis(); } } - /** - * Utility method to print the stack trace of aExp to a string - */ - public static String getStackTrace(Throwable aThrowable) - { - StringBuilder strBuf; - - strBuf = new StringBuilder(); - strBuf.append(aThrowable.getClass().getName() + "\n"); - strBuf.append("Msg: " + aThrowable.getMessage() + "\n"); - - // Print out the stack trace - for (StackTraceElement aItem : aThrowable.getStackTrace()) - strBuf.append(" " + aItem.toString() + "\n"); - - // Print out any cause - // TODO - - return strBuf.toString(); - } - - /** - * Utility method to print the stack trace of aExp to a string exactly as {@link Throwable#printStackTrace} - */ - public static String getStackTraceClassic(Throwable aThrowable) - { - StringWriter stringWriter; - PrintWriter printWriter; - - stringWriter = new StringWriter(); - printWriter = new PrintWriter(stringWriter); - aThrowable.printStackTrace(printWriter); - - printWriter.close(); - return stringWriter.toString(); - } - } diff --git a/src/glum/util/TimeConst.java b/src/glum/util/TimeConst.java index 280423b..dc67972 100644 --- a/src/glum/util/TimeConst.java +++ b/src/glum/util/TimeConst.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; public class TimeConst diff --git a/src/glum/util/WallTimer.java b/src/glum/util/WallTimer.java index f70d381..2181ae5 100644 --- a/src/glum/util/WallTimer.java +++ b/src/glum/util/WallTimer.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.util; public class WallTimer diff --git a/src/glum/version/PlainVersion.java b/src/glum/version/PlainVersion.java new file mode 100644 index 0000000..d7447f7 --- /dev/null +++ b/src/glum/version/PlainVersion.java @@ -0,0 +1,126 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.version; + +/** + * Provides the standard implementation of the {@link Version} interface. + * + * @author lopeznr1 + */ +public class PlainVersion implements Version +{ + // Constants + public static PlainVersion AbsMin = new PlainVersion(Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + public static PlainVersion AbsMax = new PlainVersion(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); + public static PlainVersion Zero = new PlainVersion(0, 0, 0); + + // Attributes + private final int major; + private final int minor; + private final int patch; + + /** Standard Constructor */ + public PlainVersion(int aMajor, int aMinor, int aPatch) + { + major = aMajor; + minor = aMinor; + patch = aPatch; + } + + /** + * Forms a PlainVersion from the specified string. The version should have have at most 3 integer components + * separated by the char: '.'. Any extra components after the first 3 will be ignored. + *

    + * If any of the first 3 components are not integers then null will be returned. + */ + public static PlainVersion parse(String aStr) + { + var tokenArr = aStr.split("\\."); + + int major = 0, minor = 0, patch = 0; + try + { + major = Integer.parseInt(tokenArr[0]); + if (tokenArr.length >= 2) + minor = Integer.parseInt(tokenArr[1]); + if (tokenArr.length >= 3) + patch = Integer.parseInt(tokenArr[2]); + } + catch (NumberFormatException aExp) + { + return null; + } + + return new PlainVersion(major, minor, patch); + } + + @Override + public int major() + { + return major; + } + + @Override + public int minor() + { + return minor; + } + + @Override + public int patch() + { + return patch; + } + + @Override + public String toString() + { + var retStr = "" + major + "." + minor; + if (patch != 0) + retStr += "." + patch; + + return retStr; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PlainVersion other = (PlainVersion) obj; + if (major != other.major) + return false; + if (minor != other.minor) + return false; + if (patch != other.patch) + return false; + return true; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + major; + result = prime * result + minor; + result = prime * result + patch; + return result; + } + +} diff --git a/src/glum/version/Version.java b/src/glum/version/Version.java new file mode 100644 index 0000000..d80ffdd --- /dev/null +++ b/src/glum/version/Version.java @@ -0,0 +1,49 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.version; + +/** + * Interface which provides access to version components (major, minor, patch). + *

    + * Each component is modeled as an integer and it is assumed that higher values correspond to more developed software. + *

    + * Reference: https://semver.org/ + *

    + * Implementors of this interface should be immutable. + * + * @author lopeznr1 + */ +public interface Version +{ + // Constants + public static Version AbsMin = PlainVersion.AbsMin; + public static Version AbsMax = PlainVersion.AbsMax; + public static Version Zero = PlainVersion.Zero; + + /** + * Returns the major version component. + */ + public int major(); + + /** + * Returns the minor version component. + */ + public int minor(); + + /** + * Returns the patch version component. + */ + public int patch(); + +} diff --git a/src/glum/version/VersionUtils.java b/src/glum/version/VersionUtils.java new file mode 100644 index 0000000..64cd50b --- /dev/null +++ b/src/glum/version/VersionUtils.java @@ -0,0 +1,109 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.version; + +/** + * Utility class that allows for comparing Versions. + *

    + * Eventually when Java allows operator overloading then this class can go away since the standard mathematical + * comparison symbols would be much clearer. + * + * @author lopeznr1 + */ +public class VersionUtils +{ + /** + * Utility method that returns true if aVerA occurs after aVerB + */ + public static boolean isAfter(Version aVerA, Version aVerB) + { + int majorA = aVerA.major(); + int minorA = aVerA.minor(); + int patchA = aVerA.patch(); + int majorB = aVerB.major(); + int minorB = aVerB.minor(); + int patchB = aVerB.patch(); + + if (majorA > majorB) + return true; + if (majorA == majorB && minorA > minorB) + return true; + if (majorA == majorB && minorA == minorB && patchA > patchB) + return true; + + return false; + } + + /** + * Utility method that returns true if aVerA occurs after aVerB + */ + public static boolean isAfterOrEqual(Version aVerA, Version aVerB) + { + // Delegate to isAfter + return isAfter(aVerB, aVerA) == false; + } + + /** + * Utility method that returns true if the following statement is true: + *

    + * aVerEval >= aVerMin && aVerEval <= aVerMax + *

    + * A LogicError will be thrown if the aVerMin and aVerMax are inverted (aVerMin > aVerMax) + */ + public static boolean isInRange(Version aVerEval, Version aVerMin, Version aVerMax) + { + // Ensure the endpoints are not inverted + if (isAfter(aVerMin, aVerMax) == true) + throw new RuntimeException("Min/Max versions appear to be swapped. min: " + aVerMin + " max: " + aVerMax); + + // Decompose and delegate + if (isAfter(aVerMin, aVerEval) == true) + return false; + if (isAfter(aVerEval, aVerMax) == true) + return false; + + return true; + } + + /** + * Utility method to allow the comparison of two versions. + * + * @param aVerA + * @param aVerB + * @return + */ + public static int compare(Version aVerA, Version aVerB) + { + + int majorA = aVerA.major(); + int minorA = aVerA.minor(); + int patchA = aVerA.patch(); + int majorB = aVerB.major(); + int minorB = aVerB.minor(); + int patchB = aVerB.patch(); + + int cmpVal; + cmpVal = majorA - majorB; + if (cmpVal != 0) + return cmpVal; + cmpVal = minorA - minorB; + if (cmpVal != 0) + return cmpVal; + cmpVal = patchA - patchB; + if (cmpVal != 0) + return cmpVal; + + return 0; + } +} diff --git a/src/glum/zio/NullableZRS.java b/src/glum/zio/NullableZRS.java new file mode 100644 index 0000000..397e5b7 --- /dev/null +++ b/src/glum/zio/NullableZRS.java @@ -0,0 +1,108 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.zio; + +import java.io.IOException; +import java.lang.reflect.Constructor; + +/** + * Generic implementation of {@link ZioSpawner} that supports "null" items. + *

    + * This implementation will support serialization of null (or invalid) items. When the deserialization process occurs a + * previously serialized null item will result in the corresponding null (or invalid) item being returned. + * + * @author lopeznr1 + */ +public class NullableZRS implements ZioSpawner +{ + // Attributes + private final Class refClass; + private final G1 refInvalidItem; + + /** + * Standard Constructor + * + * @param aClass + * Reference class associated with the items to be serialized by this {@link ZioSpawner}. + * + * @param aInvalidItem + * Null equivalent object. This may be null or an (immutable) object that stands for "invalid" values. + */ + public NullableZRS(Class aClass, G1 aInvalidItem) + { + refClass = aClass; + refInvalidItem = aInvalidItem; + } + + /** + * Simplified Constructor + *

    + * Serialized values of null (or a previously invalid item) will result in null being returned. + */ + public NullableZRS(Class aClass) + { + this(aClass, null); + } + + @Override + public G1 readInstance(ZinStream aStream) throws IOException + { + aStream.readVersion(0); + + byte type = aStream.readByte(); + if (type == 0) + return refInvalidItem; + + // Locate an appropriate Constructor + Constructor tmpConstructor = null; + try + { + tmpConstructor = refClass.getConstructor(ZinStream.class); + } + catch (NoSuchMethodException | SecurityException aExp) + { + throw new IOException("Failed to locate a proper constructor. Constuctor requires " // + + "a single argument - ZioStream", aExp); + } + + // Serialize the class + try + { + G1 retItem = tmpConstructor.newInstance(aStream); + return retItem; + } + catch (Exception aExp) + { + throw new IOException("Failed to instantiate: " + refClass, aExp); + } + + } + + @Override + public void writeInstance(ZoutStream aStream, G1 aItem) throws IOException + { + aStream.writeVersion(0); + + byte type = 0; + if (aItem != null && aItem != refInvalidItem) + type = 1; + + aStream.writeByte(type); + if (type == 0) + return; + + aItem.zioWrite(aStream); + } + +} diff --git a/src/glum/zio/ZinStream.java b/src/glum/zio/ZinStream.java index 4bd5aef..a11d986 100644 --- a/src/glum/zio/ZinStream.java +++ b/src/glum/zio/ZinStream.java @@ -1,12 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio; import java.io.IOException; +/** + * Interface that defines methods used to deserialize data using the glum.zio framework. + * + * @author lopeznr1 + */ public interface ZinStream extends AutoCloseable { /** * Releases any resources associated with the ZioInStream */ + @Override public void close() throws IOException; /** @@ -16,7 +35,7 @@ public interface ZinStream extends AutoCloseable /** * Returns the checksum (as a string) of the stream. - *

    + *

    * Note, if the stream is still open, then the returned value will be the checksum evaluated as of the last byte * grabbed from this stream (with no buffering effects - closing the stream immediately will not result in a * different value). @@ -25,9 +44,9 @@ public interface ZinStream extends AutoCloseable /** * Returns the virtual position of this stream - * + * * @throws IOException - * If the stream has been closed. + * If the stream has been closed. */ public long getPosition() throws IOException; @@ -46,6 +65,11 @@ public interface ZinStream extends AutoCloseable */ public char readChar() throws IOException; + /** + * Returns an enum from the stream. + */ + public > G1 readEnum(G1[] aValueArr) throws IOException; + /** * Returns the next int */ @@ -85,17 +109,17 @@ public interface ZinStream extends AutoCloseable /** * Returns the contents of dstArr, starting from offset to length fully. - * + * * @throws IOException - * Will be thrown if not enough data in the stream to fulfill request + * Will be thrown if not enough data in the stream to fulfill request */ public void readFully(byte[] dstArr, int offset, int length) throws IOException; /** * Reads in the contents of dstArr fully. - * + * * @throws IOException - * Will be thrown if not enough data in the stream to fulfill request + * Will be thrown if not enough data in the stream to fulfill request */ public void readFully(byte[] dstArr) throws IOException; @@ -110,16 +134,24 @@ public interface ZinStream extends AutoCloseable * Method to read the recorded version, and validate that it matches one of the values in validArr. If there is a * mismatch, then an IOException will be thrown. The recorded version should have been written with the inverse * method {@link ZoutStream#writeVersion}. - * + * * @return The version that was read in. */ public int readVersion(int... validArr) throws IOException; + /** + * Method to read the recorded version. Any value will due. The previously recorded version should have been written + * with the inverse method {@link ZoutStream#writeVersion}. + * + * @return The version that was read in. + */ + public int readVersionAny() throws IOException; + /** * Method to skip numBytes. - * + * * @throws IOException - * Will be thrown if not enough data in the stream to fulfill request + * Will be thrown if not enough data in the stream to fulfill request */ public void skipBytes(int numBytes) throws IOException; diff --git a/src/glum/zio/ZioObj.java b/src/glum/zio/ZioObj.java index e5c02d2..b34471d 100644 --- a/src/glum/zio/ZioObj.java +++ b/src/glum/zio/ZioObj.java @@ -1,19 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio; import java.io.IOException; /** * Interface to allow for serialization of (mutable) objects. + * + * @author lopeznr1 */ public interface ZioObj { /** - * Deserialization method to read data from the ZinStream. + * Deserialization method to read data from the {@link ZinStream}. */ public void zioRead(ZinStream aStream) throws IOException; /** - * Serialization method to write data to the ZoutStream. + * Serialization method to write data to the {@link ZoutStream}. */ public void zioWrite(ZoutStream aStream) throws IOException; diff --git a/src/glum/zio/ZioObjUtil.java b/src/glum/zio/ZioObjUtil.java index 672903f..1d3d1ec 100644 --- a/src/glum/zio/ZioObjUtil.java +++ b/src/glum/zio/ZioObjUtil.java @@ -1,17 +1,32 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio; import java.io.IOException; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedHashMap; -import java.util.Map; +import java.util.*; +/** + * Collection of utility methods for working with {@link ZioObj} objects. + * + * @author lopeznr1 + */ public class ZioObjUtil { /** - * Utility method to read a list of ZioObj items. The objects are assumed to be of the same type. - *

    - * Format: ()* + * Utility method to read a list of {@link ZioObj} items. The objects are assumed to be of the same type. + *

    + * Format: <numItems> (<ZioObj>)* */ public static ArrayList readList(ZinStream aStream, Class aClass) throws IOException { @@ -19,7 +34,7 @@ public class ZioObjUtil int numItems = aStream.readInt(); // Read the actual objects - ArrayList itemList = new ArrayList(); + ArrayList retItemL = new ArrayList(); for (int c1 = 0; c1 < numItems; c1++) { // Serialize the class @@ -27,7 +42,7 @@ public class ZioObjUtil { G1 aItem = aClass.getDeclaredConstructor().newInstance(); aItem.zioRead(aStream); - itemList.add(aItem); + retItemL.add(aItem); } catch(Exception aException) { @@ -35,46 +50,46 @@ public class ZioObjUtil } } - return itemList; + return retItemL; } /** * Utility method to read a preloaded list of ZioObj items. The passed in list must contain the exact number of items * as that stored on disk and in the correct order. - *

    - * Format: ()* + *

    + * Format: <numItems> (<ZioObj>)* */ - public static void readList(ZinStream aStream, Collection aItemList) throws IOException + public static void readList(ZinStream aStream, Collection aItemL) throws IOException { // Read the item count int numItems = aStream.readInt(); - if (numItems != aItemList.size()) - throw new IOException("Items stored: " + numItems + ". Expected: " + aItemList.size()); + if (numItems != aItemL.size()) + throw new IOException("Items stored: " + numItems + ". Expected: " + aItemL.size()); // Read the actual BinRaw items - for (ZioObj aItem : aItemList) + for (ZioObj aItem : aItemL) aItem.zioRead(aStream); } /** * Utility method to write out a list of ZioObj items. - *

    - * Format: ()* + *

    + * Format: <numItems> (<ZioObj>)* */ - public static void writeList(ZoutStream aStream, Collection aItemList) throws IOException + public static void writeList(ZoutStream aStream, Collection aItemL) throws IOException { // Write the item count - aStream.writeInt(aItemList.size()); + aStream.writeInt(aItemL.size()); // Write the actual objects - for (ZioObj aItem : aItemList) + for (ZioObj aItem : aItemL) aItem.zioWrite(aStream); } /** * Utility method to read a map of binary objects. The ZioObj items are assumed to be of the same type. - *

    - * Format: ()* + *

    + * Format: <numItems> (<String, ZioObj>)* */ public static Map readMap(ZinStream aStream, Class aClass) throws IOException { @@ -82,7 +97,7 @@ public class ZioObjUtil int numItems = aStream.readInt(); // Read the actual objects - Map itemMap = new LinkedHashMap(); + Map retItemM = new LinkedHashMap(); for (int c1 = 0; c1 < numItems; c1++) { // Read the key @@ -90,10 +105,10 @@ public class ZioObjUtil // Read the value G1 aItem = read(aStream, aClass); - itemMap.put(aKey, aItem); + retItemM.put(aKey, aItem); } - return itemMap; + return retItemM; } /** @@ -101,15 +116,15 @@ public class ZioObjUtil * contain as many items (and in the order) as that which will be read in from the disk. It is therefore advisable * that only LinkedHashMaps be used with this method. */ - public static void readMap(ZinStream aStream, Map aItemMap) throws IOException + public static void readMap(ZinStream aStream, Map aItemM) throws IOException { // Read the item count int numItems = aStream.readInt(); - if (numItems != aItemMap.size()) - throw new IOException("Items stored: " + numItems + ". Expected: " + aItemMap.size()); + if (numItems != aItemM.size()) + throw new IOException("Items stored: " + numItems + ". Expected: " + aItemM.size()); // Read the actual key,value pairings - String[] keyArr = aItemMap.keySet().toArray(new String[0]); + String[] keyArr = aItemM.keySet().toArray(new String[0]); for (int c1 = 0; c1 < numItems; c1++) { // Read the key and ensure the proper key was read @@ -118,29 +133,29 @@ public class ZioObjUtil throw new IOException("Key read: " + aKey + ". Expected: " + keyArr[c1]); // Read the value - ZioObj aItem = aItemMap.get(aKey); + ZioObj aItem = aItemM.get(aKey); aItem.zioRead(aStream); } } /** * Utility method to write out a map of ZioObj items. - *

    - * Format: ()* + *

    + * Format: <numItems> (<String, ZioObj>)* */ - public static void writeMap(ZoutStream aStream, Map aItemMap) throws IOException + public static void writeMap(ZoutStream aStream, Map aItemM) throws IOException { // Write the item count - aStream.writeInt(aItemMap.size()); + aStream.writeInt(aItemM.size()); // Write the actual objects - for (String aKey : aItemMap.keySet()) + for (String aKey : aItemM.keySet()) { // Write the key aStream.writeString(aKey); // Write the value - ZioObj aZioObj = aItemMap.get(aKey); + ZioObj aZioObj = aItemM.get(aKey); aZioObj.zioWrite(aStream); } } diff --git a/src/glum/zio/ZioRaw.java b/src/glum/zio/ZioRaw.java index f5c14a7..8ee160d 100644 --- a/src/glum/zio/ZioRaw.java +++ b/src/glum/zio/ZioRaw.java @@ -1,18 +1,31 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio; import java.io.IOException; /** * Interface to allow for serialization of (immutable) objects. - *

    - * The method zioWrite() is provided to allow immutable objects to be serialized. - *

    - * The method zioRead() is not provided since that would imply mutability. Any deserialization should be done via the constructor. + *

    + * Implementations of this interface would be expected to handle deserialization via the constructor. + * + * @author lopeznr1 */ public interface ZioRaw { /** - * Serialization method to write data to the ZoutStream. + * Serialization method to write data to the {@link ZoutStream}. */ public void zioWrite(ZoutStream aStream) throws IOException; diff --git a/src/glum/zio/ZioRawUtil.java b/src/glum/zio/ZioRawUtil.java deleted file mode 100644 index 704d7a8..0000000 --- a/src/glum/zio/ZioRawUtil.java +++ /dev/null @@ -1,69 +0,0 @@ -package glum.zio; - -import java.io.IOException; -import java.lang.reflect.Constructor; -import java.util.ArrayList; -import java.util.Collection; - -public class ZioRawUtil -{ - /** - * Utility method to read a list of ZioRaw items. The following requirements must be met: - *

      - *
    • The objects are assumed to be of the same type. - *
    • The class must define a Constructor that takes one argument - a ZinStream. - *
    - *

    - * Format: ()* - */ - public static ArrayList readList(ZinStream aStream, Class aClass) throws IOException - { - // Locate an appropriate Constructor - Constructor tmpConstructor = null; - try - { - tmpConstructor = aClass.getConstructor(ZinStream.class); - } - catch(NoSuchMethodException | SecurityException aExp) - { - throw new IOException("Failed to locate a proper constructor. Constuctor requires a single argument - ZioStream", aExp); - } - - // Read the item count - int numItems = aStream.readInt(); - - // Read the actual objects - ArrayList itemList = new ArrayList(); - for (int c1 = 0; c1 < numItems; c1++) - { - // Serialize the class - try - { - G1 aItem = tmpConstructor.newInstance(aStream); - itemList.add(aItem); - } - catch(Exception aException) - { - throw new IOException("Failed to instantiate: " + aClass, aException); - } - } - - return itemList; - } - - /** - * Utility method to write out a list of ZioRaw items. - *

    - * Format: ()* - */ - public static void writeList(ZoutStream aStream, Collection aItemList) throws IOException - { - // Write the item count - aStream.writeInt(aItemList.size()); - - // Write the actual objects - for (ZioRaw aItem : aItemList) - aItem.zioWrite(aStream); - } - -} diff --git a/src/glum/zio/ZioSpawner.java b/src/glum/zio/ZioSpawner.java new file mode 100644 index 0000000..0a82096 --- /dev/null +++ b/src/glum/zio/ZioSpawner.java @@ -0,0 +1,35 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.zio; + +import java.io.IOException; + +/** + * Interface that defines a mechanism for serialization of items. + * + * @author lopeznr1 + */ +public interface ZioSpawner +{ + /** + * Method that returns the appropriate item that is read from the specified {@link ZinStream}. + */ + public G1 readInstance(ZinStream aStream) throws IOException; + + /** + * Method to serialized the provided item to the specified {@link ZoutStream}. + */ + public void writeInstance(ZoutStream aStream, G1 aItem) throws IOException; + +} diff --git a/src/glum/zio/ZoutStream.java b/src/glum/zio/ZoutStream.java index 86b9941..7396bb0 100644 --- a/src/glum/zio/ZoutStream.java +++ b/src/glum/zio/ZoutStream.java @@ -1,27 +1,46 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio; import java.io.IOException; +/** + * Interface that defines methods used to serialize data using the glum.zio framework. + * + * @author lopeznr1 + */ public interface ZoutStream extends AutoCloseable { /** * Releases any resources associated with the stream */ + @Override public void close() throws IOException; /** * Returns the checksum (as a string) of the stream. - *

    - * Note, if the stream is still open, then the returned value will be the checksum evaluated as of the last byte sent to this stream (with no buffering - * effects - closing the stream immediately will not result in a different value). + *

    + * Note, if the stream is still open, then the returned value will be the checksum evaluated as of the last byte sent + * to this stream (with no buffering effects - closing the stream immediately will not result in a different value). */ public String getCheckSum() throws IOException; /** * Returns the virtual position of this stream. - * + * * @throws IOException - * If the stream has been closed. + * If the stream has been closed. */ public long getPosition() throws IOException; @@ -40,6 +59,11 @@ public interface ZoutStream extends AutoCloseable */ public void writeChar(char aChar) throws IOException; + /** + * Outputs an enum to the stream. + */ + public > void writeEnum(G1 aEnum) throws IOException; + /** * Outputs the next int */ @@ -71,29 +95,30 @@ public interface ZoutStream extends AutoCloseable public void writeString(String aStr) throws IOException; /** - * Utility method to write out a raw string. Note the inverse function is {@link ZinStream#readRawStringAndValidate}. The string will be interpreted as a - * US-ASCII string. + * Utility method to write out a raw string. Note the inverse function is {@link ZinStream#readRawStringAndValidate}. + * The string will be interpreted as a US-ASCII string. */ public void writeRawString(String aStr) throws IOException; /** * Writes the contents of dstArr, starting from offset to length fully. - * + * * @throws IOException - * Will be thrown if not enough space in the stream to fulfill request + * Will be thrown if not enough space in the stream to fulfill request */ public void writeFully(byte[] dstArr, int offset, int length) throws IOException; /** * Writes the contents of dstArr fully. - * + * * @throws IOException - * Will be thrown if not enough space in the stream to fulfill request + * Will be thrown if not enough space in the stream to fulfill request */ public void writeFully(byte[] dstArr) throws IOException; /** - * Method to write the version to the stream. To properly read the version, use the inverse function {@link ZinStream#readVersion}. + * Method to write the version to the stream. To properly read the version, use the inverse function + * {@link ZinStream#readVersion}. */ public void writeVersion(int aVersion) throws IOException; diff --git a/src/glum/zio/stream/BaseZinStream.java b/src/glum/zio/stream/BaseZinStream.java index 7fd1d0d..6612df5 100644 --- a/src/glum/zio/stream/BaseZinStream.java +++ b/src/glum/zio/stream/BaseZinStream.java @@ -1,3 +1,16 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; import java.io.IOException; @@ -14,6 +27,11 @@ import glum.util.WallTimer; import glum.zio.ZinStream; import glum.zio.util.ZioUtil; +/** + * Base implementation of the {@link ZinStream}. + * + * @author lopeznr1 + */ public abstract class BaseZinStream implements ZinStream { // Work vars @@ -47,13 +65,13 @@ public abstract class BaseZinStream implements ZinStream digest = MessageDigest.getInstance("MD5"); digestPos = 0; } - catch(NoSuchAlgorithmException aExp) + catch (NoSuchAlgorithmException aExp) { throw new IOException("Unreconized Algorithm", aExp); } } - // Allocate our work vars + // Allocate the work vars allocateWorkVars(streamSizeHint); } @@ -65,7 +83,8 @@ public abstract class BaseZinStream implements ZinStream */ public BaseZinStream(ByteBuffer aWorkBuffer, boolean computeCheckSum) throws IOException { - // Allocate the checksum digest worker + // Allocate the stat vars + wallTimer = new WallTimer(true); digest = null; checkSumStr = null; if (computeCheckSum == true) @@ -75,13 +94,13 @@ public abstract class BaseZinStream implements ZinStream digest = MessageDigest.getInstance("MD5"); digestPos = 0; } - catch(NoSuchAlgorithmException aExp) + catch (NoSuchAlgorithmException aExp) { throw new IOException("Unreconized Algorithm", aExp); } } - // Allocate our work vars + // Allocate the work vars workBuffer = aWorkBuffer; if (workBuffer == null) throw new NullPointerException(); @@ -159,6 +178,17 @@ public abstract class BaseZinStream implements ZinStream return workBuffer.getChar(); } + @Override + public > G1 readEnum(G1[] aValueArr) throws IOException + { + int ver = readVersion(0, 1); + if (ver == 0) + return null; + + int ordinal = ZioUtil.readCompactInt(this); + return aValueArr[ordinal]; + } + @Override public int readInt() throws IOException { @@ -240,7 +270,8 @@ public abstract class BaseZinStream implements ZinStream // Ensure the two arrays are equal if (Arrays.equals(absByteArr, readByteArr) == false) - throw new IOException("Mismatched string. Needed:" + absStr + " Found:" + new String(readByteArr, Charsets.US_ASCII)); + throw new IOException( + "Mismatched string. Needed:" + absStr + " Found:" + new String(readByteArr, Charsets.US_ASCII)); } @Override @@ -293,9 +324,7 @@ public abstract class BaseZinStream implements ZinStream int readVersion; // Read the version - readVersion = readByte() & 0x00FF; - if (readVersion == 255) - readVersion = readInt(); + readVersion = ZioUtil.readCompactInt(this); // Ensure the version is one of the valid versions if (readVersion == aValidVer) @@ -324,7 +353,18 @@ public abstract class BaseZinStream implements ZinStream if (validArr.length == 1) throw new IOException("Unreconized version... Read: " + readVersion + " Expected: " + validArr[0]); - throw new IOException("Unreconized version... Read: " + readVersion + " Expected one of the following: " + Arrays.toString(validArr)); + throw new IOException("Unreconized version... Read: " + readVersion + " Expected one of the following: " + + Arrays.toString(validArr)); + } + + @Override + public int readVersionAny() throws IOException + { + int readVersion; + + // Read the version + readVersion = ZioUtil.readCompactInt(this); + return readVersion; } @Override @@ -351,7 +391,7 @@ public abstract class BaseZinStream implements ZinStream /** * Helper method to refresh the workBuffer with new data from the stream. This method ensures that workBuffer will * always have enough data to support reading. - *

    + *

    * If there is no more data on the stream then this method should throw an IOException */ protected abstract void refreshWorkBuffer() throws IOException; @@ -369,14 +409,12 @@ public abstract class BaseZinStream implements ZinStream */ protected void updateDigest() throws IOException { - ByteBuffer tmpBuffer; - // Bail if the there is no digest if (digest == null) return; // Retrieve a duplicate of the workBuffer (to preserve its configuration) - tmpBuffer = workBuffer.duplicate(); + var tmpBuffer = workBuffer.duplicate(); // Evaluate the digest from the digestPos to the limit (workBuffer's current position) tmpBuffer.flip(); @@ -392,11 +430,8 @@ public abstract class BaseZinStream implements ZinStream */ private void allocateWorkVars(long streamSizeHint) throws IOException { - int workCap; - boolean isDirect; - // Determine if we should use a direct buffer for our workBuffer (stream > 25 MB) - isDirect = false; + var isDirect = false; if (streamSizeHint > 25 * 1024 * 1024) isDirect = true; @@ -404,7 +439,7 @@ public abstract class BaseZinStream implements ZinStream if (isDirect == false) { // [1K, 16K], indirect buffer - workCap = (int)streamSizeHint; + var workCap = (int) streamSizeHint; if (workCap < 1024) workCap = 1024; else if (workCap > 16 * 1024) @@ -415,10 +450,10 @@ public abstract class BaseZinStream implements ZinStream else { // 512K, direct buffer - workCap = 512 * 1024; + var workCap = 512 * 1024; workBuffer = ByteBuffer.allocateDirect(workCap); } -//System.out.println("Is direct buffer: " + workBuffer.isDirect() + " bufferCap: " + workCap); +//System.out.println("Is direct buffer: " + workBuffer.isDirect() + " bufferCap: " + workCap); // Mark the contents in workBuffer as completely empty workBuffer.limit(0); diff --git a/src/glum/zio/stream/BaseZoutStream.java b/src/glum/zio/stream/BaseZoutStream.java index 104af6a..dbb9f70 100644 --- a/src/glum/zio/stream/BaseZoutStream.java +++ b/src/glum/zio/stream/BaseZoutStream.java @@ -1,9 +1,18 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; -import glum.util.WallTimer; -import glum.zio.ZoutStream; -import glum.zio.util.ZioUtil; - import java.io.IOException; import java.math.BigInteger; import java.nio.ByteBuffer; @@ -13,6 +22,15 @@ import java.security.NoSuchAlgorithmException; import com.google.common.base.Charsets; import com.google.common.base.Strings; +import glum.util.WallTimer; +import glum.zio.ZoutStream; +import glum.zio.util.ZioUtil; + +/** + * Base implementation of the {@link ZoutStream}. + * + * @author lopeznr1 + */ public abstract class BaseZoutStream implements ZoutStream { // Work vars @@ -24,11 +42,13 @@ public abstract class BaseZoutStream implements ZoutStream private String checkSumStr; /** + * Standard Constructor + * * @param computeCheckSum - * True if a checksum (md5sum) is desired to be computed as the stream is written + * True if a checksum (md5sum) is desired to be computed as the stream is written * @param isDirect - * True if a direct buffer is desired. This should only be true if the stream is going to a physical I/O component (disk, network) and the size of - * the final stream will be at least ~50 MB. + * True if a direct buffer is desired. This should only be true if the stream is going to a physical I/O + * component (disk, network) and the size of the final stream will be at least ~50 MB. */ public BaseZoutStream(boolean computeCheckSum, boolean isDirect) throws IOException { @@ -41,7 +61,7 @@ public abstract class BaseZoutStream implements ZoutStream if (computeCheckSum == true) digest = MessageDigest.getInstance("MD5"); } - catch(NoSuchAlgorithmException aExp) + catch (NoSuchAlgorithmException aExp) { throw new IOException("Unreconized Algorithm", aExp); } @@ -110,9 +130,9 @@ public abstract class BaseZoutStream implements ZoutStream public void writeBool(boolean aBool) throws IOException { if (aBool == false) - writeByte((byte)0); + writeByte((byte) 0); else - writeByte((byte)1); + writeByte((byte) 1); } @Override @@ -125,6 +145,20 @@ public abstract class BaseZoutStream implements ZoutStream workBuffer.putChar(aChar); } + @Override + public > void writeEnum(G1 aEnum) throws IOException + { + if (aEnum == null) + { + writeVersion(0); + return; + } + + writeVersion(1); + int ordinal = aEnum.ordinal(); + ZioUtil.writeCompactInt(this, ordinal); + } + @Override public void writeInt(int aInt) throws IOException { @@ -184,14 +218,14 @@ public abstract class BaseZoutStream implements ZoutStream // Null strings are handled in special fashion if (aStr == null) { - writeShort((short)0x00FFFF); + writeShort((short) 0x00FFFF); return; } // Empty strings are handled in special fashion if (aStr.equals("") == true) { - writeShort((short)0); + writeShort((short) 0); return; } @@ -201,10 +235,11 @@ public abstract class BaseZoutStream implements ZoutStream // Ensure the string size is less than 0x00FFFF if (size >= 0x00FFFF) - throw new RuntimeException("Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); + throw new RuntimeException( + "Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); // Write out the string - writeShort((short)(size & 0x00FFFF)); + writeShort((short) (size & 0x00FFFF)); writeFully(data); } @@ -269,8 +304,9 @@ public abstract class BaseZoutStream implements ZoutStream } /** - * Helper method that ensures the digest has been updated with any buffered data. The buffer will be cleared after the digest has been updated. - *

    + * Helper method that ensures the digest has been updated with any buffered data. The buffer will be cleared after + * the digest has been updated. + *

    * The method shall be called exclusively from {@link BaseZoutStream#emptyWorkBuffer()}. */ protected void clearWorkBuffer() @@ -287,19 +323,20 @@ public abstract class BaseZoutStream implements ZoutStream } /** - * Helper method to empty the workBuffer and copy the contents to the stream. The contents of the workBuffer will be output to the "stream". This method - * ensures that workBuffer will always have enough data to support writing + * Helper method to empty the workBuffer and copy the contents to the stream. The contents of the workBuffer will be + * output to the "stream". This method ensures that workBuffer will always have enough data to support writing */ protected abstract void emptyWorkBuffer() throws IOException; /** - * Helper method to release any stream related vars. This method will only be called once, the very first time the method {@link #close()} is called. + * Helper method to release any stream related vars. This method will only be called once, the very first time the + * method {@link #close()} is called. */ protected abstract void releaseStreamVars() throws IOException; /** * Helper method to allocate our work vars. - * + * * @throws IOException */ private void allocateWorkVars(boolean isDirect) throws IOException @@ -319,7 +356,7 @@ public abstract class BaseZoutStream implements ZoutStream workCap = 512 * 1024; workBuffer = ByteBuffer.allocateDirect(workCap); } -//System.out.println("Is direct buffer: " + workBuffer.isDirect() + " bufferCap: " + workCap); +//System.out.println("Is direct buffer: " + workBuffer.isDirect() + " bufferCap: " + workCap); // Mark the buffers as empty workBuffer.clear(); diff --git a/src/glum/zio/stream/ByteArrayZinStream.java b/src/glum/zio/stream/ByteArrayZinStream.java index 51a10f9..dae5a64 100644 --- a/src/glum/zio/stream/ByteArrayZinStream.java +++ b/src/glum/zio/stream/ByteArrayZinStream.java @@ -1,19 +1,41 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; import java.io.EOFException; import java.io.IOException; import java.nio.ByteBuffer; +import glum.zio.ZinStream; + +/** + * Implementation of {@link ZinStream} backed by a byte array. + * + * @author lopeznr1 + */ public class ByteArrayZinStream extends BaseZinStream { + /** Standard Constructor */ public ByteArrayZinStream(byte[] aDataArr, boolean computeCheckSum) throws IOException { super(ByteBuffer.wrap(aDataArr), computeCheckSum); - + // Move the position to the start workBuffer.rewind(); } + /** Simplified Constructor */ public ByteArrayZinStream(byte[] aDataArr) throws IOException { this(aDataArr, false); @@ -24,7 +46,7 @@ public class ByteArrayZinStream extends BaseZinStream { if (workBuffer == null) return 0; - + return workBuffer.remaining(); } @@ -34,7 +56,7 @@ public class ByteArrayZinStream extends BaseZinStream // There is no virtual position if the stream has been closed if (workBuffer == null) throw new IOException("Stream has been closed."); - + return workBuffer.position(); } @@ -44,7 +66,7 @@ public class ByteArrayZinStream extends BaseZinStream // There will never be new fresh data for a ByteArrayZinStream throw new EOFException("EOF reached on stream."); } - + @Override protected void releaseStreamVars() throws IOException { diff --git a/src/glum/zio/stream/ByteArrayZoutStream.java b/src/glum/zio/stream/ByteArrayZoutStream.java index 9ac976b..2b2e298 100644 --- a/src/glum/zio/stream/ByteArrayZoutStream.java +++ b/src/glum/zio/stream/ByteArrayZoutStream.java @@ -1,23 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; import java.io.IOException; import java.util.Arrays; +import glum.zio.ZoutStream; + +/** + * Implementation of {@link ZoutStream} backed by a byte array. + * + * @author lopeznr1 + */ public class ByteArrayZoutStream extends BaseZoutStream { // Stream vars private byte[] dataArr; private int dataPos; + /** Standard Constructor */ public ByteArrayZoutStream(int initCap, boolean computeCheckSum) throws IOException { super(computeCheckSum, false); - + // Set up the stream vars dataArr = new byte[initCap]; dataPos = 0; } + /** Simplified Constructor */ public ByteArrayZoutStream(int initCap) throws IOException { this(initCap, false); @@ -34,7 +56,7 @@ public class ByteArrayZoutStream extends BaseZoutStream emptyWorkBuffer(); return Arrays.copyOf(dataArr, dataPos); } - + // Return the final byte array return dataArr; } @@ -75,7 +97,7 @@ public class ByteArrayZoutStream extends BaseZoutStream // Clear the workBuffer clearWorkBuffer(); } - + @Override protected void releaseStreamVars() throws IOException { diff --git a/src/glum/zio/stream/DebugZoutStream.java b/src/glum/zio/stream/DebugZoutStream.java index 1344cbf..a050f0f 100644 --- a/src/glum/zio/stream/DebugZoutStream.java +++ b/src/glum/zio/stream/DebugZoutStream.java @@ -1,15 +1,32 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; -import glum.zio.ZoutStream; - import java.io.IOException; import com.google.common.base.Charsets; +import glum.zio.ZoutStream; +import glum.zio.util.ZioUtil; + /** - * ZoutStream used for debugging. This is useful when trying to determine when and where a specific byte is being written out. - *

    - * When the specified nth byte has been written this object will throw an IOException. + * {@link ZoutStream} used for debugging. This is useful when trying to determine when and where a specific byte is + * being written out. + *

    + * When the specified nth byte has been written this object will throw an {@link IOException}. + * + * @author lopeznr1 */ public class DebugZoutStream implements ZoutStream { @@ -17,8 +34,10 @@ public class DebugZoutStream implements ZoutStream private int failByteCnt; /** + * Standard Constructor + * * @param aFailByteCnt - * The nth byte that when written will cause an Exception to be raised. + * The nth byte that when written will cause an {@link IOException} to be raised. */ public DebugZoutStream(int aFailByteCnt) { @@ -76,6 +95,20 @@ public class DebugZoutStream implements ZoutStream throwBadByteWrittenException(); } + @Override + public > void writeEnum(G1 aEnum) throws IOException + { + if (aEnum == null) + { + writeVersion(0); + return; + } + + writeVersion(1); + int ordinal = aEnum.ordinal(); + ZioUtil.writeCompactInt(this, ordinal); + } + @Override public void writeInt(int aInt) throws IOException { @@ -146,7 +179,8 @@ public class DebugZoutStream implements ZoutStream // Ensure the string size is less than 0x00FFFF if (size >= 0x00FFFF) - throw new RuntimeException("Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); + throw new RuntimeException( + "Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); byteCnt += 2 + size; if (byteCnt >= failByteCnt) diff --git a/src/glum/zio/stream/FileZinStream.java b/src/glum/zio/stream/FileZinStream.java index 57ce585..7d3839d 100644 --- a/src/glum/zio/stream/FileZinStream.java +++ b/src/glum/zio/stream/FileZinStream.java @@ -1,26 +1,45 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; -import java.io.EOFException; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; +import java.io.*; import java.nio.channels.FileChannel; +import glum.zio.ZinStream; + +/** + * Implementation of {@link ZinStream} backed by a file. + * + * @author lopeznr1 + */ public class FileZinStream extends BaseZinStream { // Stream vars private FileChannel fileCh; private byte[] staleArr; - public FileZinStream(File aFile, boolean computeCheckSum) throws IOException + /** Standard Constructor */ + public FileZinStream(File aFile, boolean aComputeCheckSum) throws IOException { - super(computeCheckSum, aFile.length()); + super(aComputeCheckSum, aFile.length()); // Set up the stream vars fileCh = new FileInputStream(aFile).getChannel(); staleArr = new byte[256]; } + /** Simplified Constructor */ public FileZinStream(File aFile) throws IOException { this(aFile, false); @@ -48,13 +67,11 @@ public class FileZinStream extends BaseZinStream @Override protected void refreshWorkBuffer() throws IOException { - int numReadBytes, numStaleBytes; - // Ensure the digest has been updated before refreshing the buffer updateDigest(); // Copies the remaining data from workBuffer to a byte (stale) array - numStaleBytes = workBuffer.remaining(); + var numStaleBytes = workBuffer.remaining(); if (numStaleBytes > 0) workBuffer.get(staleArr, 0, numStaleBytes); @@ -64,7 +81,7 @@ public class FileZinStream extends BaseZinStream workBuffer.put(staleArr, 0, numStaleBytes); // Fill the remaining workBuffer with data from the "stream" - numReadBytes = fileCh.read(workBuffer); + var numReadBytes = fileCh.read(workBuffer); if (numReadBytes == 0) System.out.println("Failed to read any buffer bytes!!! Bytes formerly read: " + numReadBytes); if (numReadBytes == -1) diff --git a/src/glum/zio/stream/FileZoutStream.java b/src/glum/zio/stream/FileZoutStream.java index ce96b98..64b3b5f 100644 --- a/src/glum/zio/stream/FileZoutStream.java +++ b/src/glum/zio/stream/FileZoutStream.java @@ -1,23 +1,43 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.nio.channels.FileChannel; +import glum.zio.ZoutStream; + +/** + * Implementation of {@link ZoutStream} backed by a file. + * + * @author lopeznr1 + */ public class FileZoutStream extends BaseZoutStream { // Stream vars private FileChannel fileCh; - public FileZoutStream(File aFile, boolean computeCheckSum, boolean isDirect) throws IOException + /** Standard Constructor */ + public FileZoutStream(File aFile, boolean aComputeCheckSum, boolean aIsDirect) throws IOException { - super(computeCheckSum, isDirect); + super(aComputeCheckSum, aIsDirect); // Set up the stream vars fileCh = new FileOutputStream(aFile).getChannel(); } + /** Simplified Constructor */ public FileZoutStream(File aFile) throws IOException { this(aFile, false, false); @@ -36,18 +56,17 @@ public class FileZoutStream extends BaseZoutStream @Override protected void emptyWorkBuffer() throws IOException { - int bytesWritten; - // Prepare the buffer for a dump of its contents from the start workBuffer.flip(); // Copy the contents of workBuffer to the stream (fileCh) - bytesWritten = 0; + var bytesWritten = 0; while (workBuffer.remaining() > 0) { bytesWritten = fileCh.write(workBuffer); if (workBuffer.remaining() > 0) - System.out.println("Failed to write buffer all at once. bytesToWrite: " + workBuffer.remaining() + ". Bytes formerly written: " + bytesWritten); + System.out.println("Failed to write buffer all at once. bytesToWrite: " + workBuffer.remaining() // + + ". Bytes formerly written: " + bytesWritten); } // Clear the workBuffer diff --git a/src/glum/zio/stream/NullZoutStream.java b/src/glum/zio/stream/NullZoutStream.java index 338fa9b..75683fe 100644 --- a/src/glum/zio/stream/NullZoutStream.java +++ b/src/glum/zio/stream/NullZoutStream.java @@ -1,18 +1,37 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.stream; -import glum.zio.ZoutStream; - import java.io.IOException; import com.google.common.base.Charsets; +import glum.zio.ZoutStream; +import glum.zio.util.ZioUtil; + /** - * ZoutStream used to count bytes that would be written. + * {@link ZoutStream} used to count bytes that would be written. + *

    + * Content is not written to any resource. + * + * @author lopeznr1 */ public class NullZoutStream implements ZoutStream { private int byteCount; + /** Standard Constructor */ public NullZoutStream() { byteCount = 0; @@ -62,6 +81,20 @@ public class NullZoutStream implements ZoutStream byteCount += 2; } + @Override + public > void writeEnum(G1 aEnum) throws IOException + { + if (aEnum == null) + { + writeVersion(0); + return; + } + + writeVersion(1); + int ordinal = aEnum.ordinal(); + ZioUtil.writeCompactInt(this, ordinal); + } + @Override public void writeInt(int aInt) throws IOException { @@ -118,7 +151,8 @@ public class NullZoutStream implements ZoutStream // Ensure the string size is less than 0x00FFFF if (size >= 0x00FFFF) - throw new RuntimeException("Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); + throw new RuntimeException( + "Transformed UTF-8 string is too large! Max size: " + (0x00FFFF - 1) + " Curr size:" + size); byteCount += 2 + size; } diff --git a/src/glum/zio/util/WrapInputStream.java b/src/glum/zio/util/WrapInputStream.java index 648c833..5588499 100644 --- a/src/glum/zio/util/WrapInputStream.java +++ b/src/glum/zio/util/WrapInputStream.java @@ -1,15 +1,29 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; +import glum.zio.ZinStream; import glum.zio.stream.BaseZinStream; /** - * Package private class to transform an InputStream to a ZinStream (view). + * Package private class to transform an {@link InputStream} to a {@link ZinStream} (view). + * + * @author lopeznr1 */ class WrapInputStream extends BaseZinStream { @@ -18,9 +32,10 @@ class WrapInputStream extends BaseZinStream private byte[] staleArr; private long streamPos; - protected WrapInputStream(InputStream aStream, boolean computeCheckSum) throws IOException + /** Standard Constructor */ + protected WrapInputStream(InputStream aStream, boolean aComputeCheckSum) throws IOException { - super(computeCheckSum, 0); + super(aComputeCheckSum, 0); // Set up the stream vars refCh = Channels.newChannel(aStream); @@ -28,6 +43,7 @@ class WrapInputStream extends BaseZinStream streamPos = 0; } + /** Simplified Constructor */ protected WrapInputStream(InputStream aStream) throws IOException { this(aStream, false); diff --git a/src/glum/zio/util/WrapOutputStream.java b/src/glum/zio/util/WrapOutputStream.java index 20ccf27..f8004a2 100644 --- a/src/glum/zio/util/WrapOutputStream.java +++ b/src/glum/zio/util/WrapOutputStream.java @@ -1,30 +1,48 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; -import glum.zio.stream.BaseZoutStream; - import java.io.IOException; import java.io.OutputStream; import java.nio.channels.Channels; import java.nio.channels.WritableByteChannel; +import glum.zio.ZoutStream; +import glum.zio.stream.BaseZoutStream; + /** - * Package private class to transform an OutputStream to a ZoutStream (view). + * Package private class to transform an {@link OutputStream} to a {@link ZoutStream} (view). + * + * @author lopeznr1 */ -public class WrapOutputStream extends BaseZoutStream +class WrapOutputStream extends BaseZoutStream { // Stream vars private WritableByteChannel refCh; private long streamPos; - protected WrapOutputStream(OutputStream aStream, boolean computeCheckSum, boolean isDirect) throws IOException + /** Standard Constructor */ + protected WrapOutputStream(OutputStream aStream, boolean aComputeCheckSum, boolean aIsDirect) throws IOException { - super(computeCheckSum, isDirect); + super(aComputeCheckSum, aIsDirect); // Set up the stream vars refCh = Channels.newChannel(aStream); streamPos = 0; } + /** Simplified Constructor */ protected WrapOutputStream(OutputStream aStream) throws IOException { this(aStream, false, false); @@ -54,7 +72,8 @@ public class WrapOutputStream extends BaseZoutStream { bytesWritten = refCh.write(workBuffer); if (workBuffer.remaining() > 0) - System.out.println("Failed to write buffer all at once. bytesToWrite: " + workBuffer.remaining() + ". Bytes formerly written: " + bytesWritten); + System.out.println("Failed to write buffer all at once. bytesToWrite: " + workBuffer.remaining() + + ". Bytes formerly written: " + bytesWritten); } // Clear the workBuffer @@ -65,7 +84,7 @@ public class WrapOutputStream extends BaseZoutStream protected void releaseStreamVars() throws IOException { refCh.close(); - + refCh = null; streamPos = -1; } diff --git a/src/glum/zio/util/WrapZinStream.java b/src/glum/zio/util/WrapZinStream.java index acb800c..4e518e4 100644 --- a/src/glum/zio/util/WrapZinStream.java +++ b/src/glum/zio/util/WrapZinStream.java @@ -1,17 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; -import glum.zio.ZinStream; - import java.io.IOException; import java.io.InputStream; +import glum.zio.ZinStream; + /** - * Package private class to transform a ZinStream to an InputStream (view). + * Package private class to transform a {@link ZinStream} to an {@link InputStream} (view). + * + * @author lopeznr1 */ class WrapZinStream extends InputStream { - private ZinStream refStream; + // Attributes + private final ZinStream refStream; + /** Standard Constructor */ protected WrapZinStream(ZinStream aStream) { refStream = aStream; @@ -32,36 +49,36 @@ class WrapZinStream extends InputStream return 0x00FF & refStream.readByte(); } - + @Override public int read(byte[] b, int off, int len) throws IOException { - long availBytes; - - availBytes = refStream.getAvailable(); + long availBytes; + + availBytes = refStream.getAvailable(); if (availBytes == 0) return -1; - + if (len > availBytes) - len = (int)availBytes; + len = (int) availBytes; refStream.readFully(b, off, len); return len; } - + @Override public long skip(long len) throws IOException { - long availBytes; - - availBytes = refStream.getAvailable(); + long availBytes; + + availBytes = refStream.getAvailable(); if (availBytes == 0) return -1; - + if (len > availBytes) len = availBytes; - - refStream.skipBytes((int)len); + + refStream.skipBytes((int) len); return len; } } \ No newline at end of file diff --git a/src/glum/zio/util/WrapZoutStream.java b/src/glum/zio/util/WrapZoutStream.java index 8bb4c21..169c6f7 100644 --- a/src/glum/zio/util/WrapZoutStream.java +++ b/src/glum/zio/util/WrapZoutStream.java @@ -1,17 +1,34 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; -import glum.zio.ZoutStream; - import java.io.IOException; import java.io.OutputStream; +import glum.zio.ZoutStream; + /** - * Package private class to transform a ZoutStream to an OutputStream (view). + * Package private class to transform a {@link ZoutStream} to an {@link OutputStream} (view). + * + * @author lopeznr1 */ class WrapZoutStream extends OutputStream { - private ZoutStream refStream; + // Attributes + private final ZoutStream refStream; + /** Standard Constructor */ protected WrapZoutStream(ZoutStream aStream) { refStream = aStream; @@ -27,9 +44,9 @@ class WrapZoutStream extends OutputStream @Override public void write(int b) throws IOException { - refStream.writeByte((byte)(b & 0x00FF)); + refStream.writeByte((byte) (b & 0x00FF)); } - + @Override public void write(byte[] b, int off, int len) throws IOException { diff --git a/src/glum/zio/util/ZioRawUtil.java b/src/glum/zio/util/ZioRawUtil.java new file mode 100644 index 0000000..6559a23 --- /dev/null +++ b/src/glum/zio/util/ZioRawUtil.java @@ -0,0 +1,157 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package glum.zio.util; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.util.ArrayList; +import java.util.Collection; + +import glum.zio.*; + +/** + * Collection of utility methods for working with {@link ZioRaw} objects. + * + * @author lopeznr1 + */ +public class ZioRawUtil +{ + /** + * Utility method to read a list of {@link ZioRaw} items. The following requirements must be met: + *

      + *
    • The objects are assumed to be of the same type. + *
    • The class must define a Constructor that takes one argument - a {@link ZinStream}. + *
    + *

    + * Format: <numItems> (<ZioRaw>)* + */ + public static ArrayList readList(ZinStream aStream, Class aClass) throws IOException + { + // Locate an appropriate Constructor + Constructor tmpConstructor = null; + try + { + tmpConstructor = aClass.getConstructor(ZinStream.class); + } + catch (NoSuchMethodException | SecurityException aExp) + { + throw new IOException("Failed to locate a proper constructor. Constuctor requires " // + + "a single argument - ZioStream", aExp); + } + + // Read the item count + int numItems = aStream.readInt(); + + // Read the actual objects + ArrayList retItemL = new ArrayList(); + for (int c1 = 0; c1 < numItems; c1++) + { + // Serialize the class + try + { + G1 aItem = tmpConstructor.newInstance(aStream); + retItemL.add(aItem); + } + catch (Exception aException) + { + throw new IOException("Failed to instantiate: " + aClass, aException); + } + } + + return retItemL; + } + + /** + * Utility method to write out a list of {@link ZioRaw} items. + *

    + * Format: <numItems> (<ZioObj>)* + */ + public static void writeList(ZoutStream aStream, Collection aItemC) throws IOException + { + // Write the item count + aStream.writeInt(aItemC.size()); + + // Write the actual objects + for (ZioRaw aItem : aItemC) + aItem.zioWrite(aStream); + } + + /** + * Utility method to write an individual {@link ZioRaw} item. + */ + public static G1 readRaw(ZinStream aStream, ZioSpawner aSpawner) throws IOException + { + aStream.readVersion(0); + return aSpawner.readInstance(aStream); + } + + /** + * Utility method to write an individual {@link ZioRaw} item. + */ + public static void writeRaw(ZoutStream aStream, ZioSpawner aSpawner, G1 aItem) throws IOException + { + aStream.writeVersion(0); + aSpawner.writeInstance(aStream, aItem); + } + + /** + * Utility method to read a list of {@link ZioRaw} items. + *

    + * Format: (<ZioRaw>)* + */ + public static ArrayList readRawList(ZinStream aStream, ZioSpawner aSpawner) throws IOException + { + ArrayList retItemL; + G1 aItem; + int numItems; + + // Read the item count + numItems = aStream.readInt(); + + // Read the actual objects + retItemL = new ArrayList<>(numItems + 1); + for (int c1 = 0; c1 < numItems; c1++) + { + // Serialize the object + try + { + aItem = aSpawner.readInstance(aStream); + retItemL.add(aItem); + } + catch (Exception aException) + { + throw new IOException("Failed to instantiate: " + aSpawner, aException); + } + } + + return retItemL; + } + + /** + * Utility method to write out a list of {@link ZioRaw} items. + *

    + * Format: (<ZioRaw>)* + */ + public static void writeRawList(ZoutStream aStream, ZioSpawner aSpawner, Collection aItemC) + throws IOException + { + // Write the item count + aStream.writeInt(aItemC.size()); + + // Write the actual objects + for (G1 aItem : aItemC) + aSpawner.writeInstance(aStream, aItem); + } + +} diff --git a/src/glum/zio/util/ZioUtil.java b/src/glum/zio/util/ZioUtil.java index 80677e8..3273104 100644 --- a/src/glum/zio/util/ZioUtil.java +++ b/src/glum/zio/util/ZioUtil.java @@ -1,5 +1,22 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; +import java.awt.*; +import java.io.IOException; +import java.time.*; + import glum.task.Task; import glum.unit.TimeCountUnit; import glum.zio.ZinStream; @@ -7,11 +24,11 @@ import glum.zio.ZoutStream; import glum.zio.stream.FileZinStream; import glum.zio.stream.FileZoutStream; -import java.awt.Color; -import java.awt.Dimension; -import java.awt.Point; -import java.io.IOException; - +/** + * Collection of utility methods for working with zio streams ({@link ZinStream} and {@link ZoutStream}). + * + * @author lopeznr1 + */ public class ZioUtil { /** @@ -32,7 +49,7 @@ public class ZioUtil checksum = aStream.getCheckSum(); runTime = aStream.getRunTime(); message = "[Read] MD5: " + checksum + " Source: " + aSource + " Time: " + aUnit.getString(runTime); - aTask.infoAppendln(message); + aTask.logRegln(message); } /** @@ -53,7 +70,7 @@ public class ZioUtil checksum = aStream.getCheckSum(); runTime = aStream.getRunTime(); message = "[Read] MD5: " + checksum + " Source: " + aSource + " Time: " + aUnit.getString(runTime); - aTask.infoAppendln(message); + aTask.logRegln(message); } /** @@ -81,9 +98,9 @@ public class ZioUtil byte byteVal; // Takes up one byte if in the range of (0, 254) - byteVal = (byte)0x00FF; + byteVal = (byte) 0x00FF; if (aValue >= 0 && aValue < 255) - byteVal = (byte)(0x00FF & aValue); + byteVal = (byte) (0x00FF & aValue); aStream.writeByte(byteVal); // Takes up 5 bytes otherwise @@ -186,20 +203,119 @@ public class ZioUtil if (aColor == null) { - aStream.writeByte((byte)0); + aStream.writeByte((byte) 0); return; } - aStream.writeByte((byte)1); + aStream.writeByte((byte) 1); - byteVal = (byte)(0x00FF & aColor.getRed()); + byteVal = (byte) (0x00FF & aColor.getRed()); aStream.writeByte(byteVal); - byteVal = (byte)(0x00FF & aColor.getGreen()); + byteVal = (byte) (0x00FF & aColor.getGreen()); aStream.writeByte(byteVal); - byteVal = (byte)(0x00FF & aColor.getBlue()); + byteVal = (byte) (0x00FF & aColor.getBlue()); aStream.writeByte(byteVal); } + /** + * Utility method to read a {@link LocalDate} from the specified stream. + */ + public static LocalDate readDate(ZinStream aStream) throws IOException + { + int ver = aStream.readVersion(0, 1); + if (ver == 0) + return null; + + int year = aStream.readShort(); + int dayOfYear = aStream.readShort(); + return LocalDate.ofYearDay(year, dayOfYear); + } + + /** + * Utility method to write a {@link LocalDate} to the specified stream. + */ + public static void writeDate(ZoutStream aStream, LocalDate aDate) throws IOException + { + if (aDate == null) + { + aStream.writeVersion(0); + return; + } + + short year = (short) aDate.getYear(); + short dayOfYear = (short) aDate.getDayOfYear(); + + aStream.writeVersion(1); + aStream.writeShort(year); + aStream.writeShort(dayOfYear); + } + + /** + * Utility method to read a {@link LocalTime} from the specified stream. + */ + public static LocalTime readTime(ZinStream aStream) throws IOException + { + int ver = aStream.readVersion(0, 1); + if (ver == 0) + return null; + + int hour = aStream.readByte(); + int min = aStream.readByte(); + int sec = aStream.readByte(); + return LocalTime.of(hour, min, sec); + } + + /** + * Utility method to write a {@link LocalTime} to the specified stream. + */ + public static void writeTime(ZoutStream aStream, LocalTime aTime) throws IOException + { + if (aTime == null) + { + aStream.writeVersion(0); + return; + } + + byte hour = (byte) aTime.getHour(); + byte min = (byte) aTime.getMinute(); + byte sec = (byte) aTime.getSecond(); + + aStream.writeVersion(1); + aStream.writeByte(hour); + aStream.writeByte(min); + aStream.writeByte(sec); + } + + /** + * Utility method to read a {@link LocalDateTime}from the specified stream. + */ + public static LocalDateTime readDateTime(ZinStream aStream) throws IOException + { + int ver = aStream.readVersion(0, 1); + if (ver == 0) + return null; + + LocalDate tmpDate = readDate(aStream); + LocalTime tmpTime = readTime(aStream); + return LocalDateTime.of(tmpDate, tmpTime); + } + + /** + * Utility method to write a {@link LocalDateTime} to the specified stream. + */ + public static void writeDateTime(ZoutStream aStream, LocalDateTime aDateTime) throws IOException + { + if (aDateTime == null) + { + aStream.writeVersion(0); + return; + } + + aStream.writeVersion(1); + writeDate(aStream, aDateTime.toLocalDate()); + writeTime(aStream, aDateTime.toLocalTime()); + } + } diff --git a/src/glum/zio/util/ZioWrapUtil.java b/src/glum/zio/util/ZioWrapUtil.java index c50c08b..f86520d 100644 --- a/src/glum/zio/util/ZioWrapUtil.java +++ b/src/glum/zio/util/ZioWrapUtil.java @@ -1,21 +1,33 @@ +// Copyright (C) 2024 The Johns Hopkins University Applied Physics Laboratory LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. package glum.zio.util; +import java.io.*; + import glum.zio.ZinStream; import glum.zio.ZoutStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - /** - * Utility class that takes a ZinStream/ZoutStream and presents various InputStream/OutputStream views. + * Utility class that takes a {@link ZinStream} / {@link ZoutStream} and presents various {@link InputStream} / + * {@link OutputStream} views. + * + * @author lopeznr1 */ public class ZioWrapUtil { /** - * Utility method to return the ZinStream as a DataInputStream view + * Utility method to return the {@link ZinStream} as a {@link DataInputStream} view */ public static DataInputStream asDataInputStream(ZinStream aStream) { @@ -23,7 +35,7 @@ public class ZioWrapUtil } /** - * Utility method to return the ZinStream as a DataInputStream view + * Utility method to return the {@link ZinStream} as a {@link DataInputStream} view */ public static DataOutputStream asDataOutputStream(ZoutStream aStream) { @@ -31,7 +43,7 @@ public class ZioWrapUtil } /** - * Utility method to return the ZinStream as an InputStream view + * Utility method to return the {@link ZinStream} as an {@link InputStream} view */ public static InputStream asInputStream(ZinStream aStream) { @@ -39,15 +51,15 @@ public class ZioWrapUtil } /** - * Utility method to return the ZoutStream as an OutputStream view + * Utility method to return the {@link ZoutStream} as an {@link OutputStream} view */ public static OutputStream asOutputStream(ZoutStream aStream) { return new WrapZoutStream(aStream); } - + /** - * Utility method to return InputStream the as a ZinStream view + * Utility method to return {@link InputStream} the as a {@link ZinStream} view */ public static ZinStream asZinStream(InputStream aStream) throws IOException { diff --git a/src/module-info.java.unused b/src/module-info.java.unused new file mode 100644 index 0000000..50bb9a2 --- /dev/null +++ b/src/module-info.java.unused @@ -0,0 +1,38 @@ +module glum +{ + requires java.base; + requires java.compiler; + requires transitive java.desktop; +// requires static java.desktop; + requires guava; + requires miglayout; +// requires miglayout; +// requires net.miginfocom; +// requires net.miginfocom.layout; + + exports glum.gui; + exports glum.io; + exports glum.io.token; + exports glum.net; + exports glum.unit; + exports glum.util; + exports glum.reflect; + exports glum.registry; + exports glum.task; + + exports glum.database; + exports glum.gui.action; + exports glum.gui.component; + exports glum.gui.panel; + exports glum.gui.panel.generic; + exports glum.gui.panel.itemList; + exports glum.gui.panel.itemList.query; + exports glum.gui.panel.task; + exports glum.zio; + exports glum.zio.stream; + exports glum.zio.util; + +// uses java.awt.event.ActionListener; + uses java.awt.Font; + uses java.awt.Component; +} diff --git a/tools/build.xml b/tools/build.xml new file mode 100644 index 0000000..bc3bfe4 --- /dev/null +++ b/tools/build.xml @@ -0,0 +1,66 @@ + + + + + + + Glum Library + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/buildRelease b/tools/buildRelease new file mode 100755 index 0000000..5cb03d2 --- /dev/null +++ b/tools/buildRelease @@ -0,0 +1,305 @@ +#! /usr/bin/env python3 + +import argparse +import datetime +import fileinput +import glob +import os +import re +import shutil +import signal +import subprocess +import sys +import time +import traceback + + +# Define the (baseline) version +baseVersion = "2.0.0" + +# Define relevant base names +appBaseName = 'Glum' +libBaseName = 'glum' + +# Define the libraries Glum depends on +libList = ['guava-18.0.jar', 'miglayout-3.7.2-swing.jar'] + +# Define the paths to various executables +antPath = '/spare/apache/apache-ant-1.10.8' +antExe = os.path.join(antPath, 'bin/ant') +jdkPath = '/spare/jdk-17' +jarExe = os.path.join(jdkPath, 'bin/jar') +javaExe = os.path.join(jdkPath, 'bin/java') + + +def checkForInstalledApps(): + """Checks for installed applications needed to build a release of the + Glum library. + """ + # Ensure required applications are installed + errList = [] + for aPath in [antExe, jarExe, javaExe]: + if os.path.exists(aPath) == False: + errList.append('System executable is missing: ' + aPath) + if len(errList) > 0: + print('There are configuration errors with the environment or system.') +# print('System Path:' + str(sys.path)) + print('Please correct the following:') + for aError in errList: + print('\t' + aError) + sys.exit(0) + + +def buildLibraryJar(aVersion): + """Method that builds the library jars. Upon sucessful execution + jar files (binary + source) will be generated. + """ + # Note it is assumed that this is run from the path /proj/glum/ + + # Compile the java files + # Run the Ant script to build the class files + os.environ["JAVA_HOME"] = jdkPath + exeCmd = [antExe, '-f', 'tools/build.xml', 'compile'] + retCode = subprocess.call(exeCmd) + if retCode != 0: + print('Failed while trying to run ant script. Exiting...\n') + sys.exit(0) + print('Finished executing Ant script.\n') + + # Generate the manifest file + manifestFN = 'release/Manifest.txt' +# buildManifestFile(manifestFN) + + # Build the (bin) jar file + jarBinFN = 'release/' + libBaseName + '-' + aVersion + '.jar' + exeCmd = [jarExe, 'cfM', jarBinFN, '-C', 'bin', '.'] + retCode = subprocess.call(exeCmd) + if retCode != 0: + print('Failed to build jar file: ' + jarBinFN) + exit(-1) + + # Build the (src) jar file + jarSrcFN = 'release/' + libBaseName + '-' + aVersion + '-src.jar' + exeCmd = ['zip', '-D9q', '../' + jarSrcFN, '-r', '.'] + retCode = subprocess.call(exeCmd, cwd='./src') + if retCode != 0: + print('Failed to build jar file: ' + jarSrcFN) + exit(-1) + + # Remove the manifest file +# os.remove(manifestFN) + + +def buildManifestFile(aManifestFN): + """Generates the manifest file that is placed within the application jar. + """ + manifestFile = open(aManifestFN, 'w') + manifestFile.write('Manifest-Version: 1.0\n') + manifestFile.write('Comment-Info: Auto generated by buildRelease\n') + + exeDate = time.localtime() + buildDateStr = time.strftime('%Y%b%d %H:%M:%S', exeDate) + manifestFile.write('Comment-Time: ' + buildDateStr + '\n') +# libStr = ' '.join('lib/' + aName for aName in libList) +# manifestFile.write('Class-Path: ' + libStr + '\n') +# manifestFile.write('Main-Class: glum.app.AppGlum\n') + manifestFile.close() + + +def buildRelease(aExtraTag, aDoNotClean=False): + """Method that builds a release of Glum. Upon sucessful execution + the following will be created: + - glum-.jar + - glum--src.jar + """ + # Define the version to build + # Retrieve the date stamp to utilize as the base version for this release +# zios_2021Jan: Should we utilize chrono versioning or major.minor.path versioning + exeDate = datetime.date.today() +# baseVersion = exeDate.strftime('%Y.%m.%d') + version = baseVersion + if aExtraTag != None: + if aExtraTag.startswith('.') or aExtraTag.startswith('-'): + version = baseVersion + aExtraTag + else: + version = baseVersion + '-' + aExtraTag + + # Retrieve the install path + installPath = getInstallRoot() + installPath = os.path.dirname(installPath) + + # Define the paths of interest + jarBinPathFN = os.path.join(installPath, 'release', libBaseName + '-' + version + '.jar') + jarSrcPathFN = os.path.join(installPath, 'release', libBaseName + '-' + version + '-src.jar') + + # Let the user know of the version we are building + print('Building ' + appBaseName + ' release ' + version + '...\n') + + # Bail if there is a prior build + failMsgL = [] + if os.path.exists(jarBinPathFN) == True: + failMsgL += ['Library binary file already exists: ' + jarBinPathFN] + if os.path.exists(jarSrcPathFN) == True: + failMsgL += ['Library source file already exists: ' + jarSrcPathFN] + + if len(failMsgL) > 0: + errPrintln('Aborting ' + appBaseName + ' release build. Reasons:') + for aFailMsg in failMsgL: + errPrintln(' - ' + aFailMsg) + errPrintln('') + exit(-1) + +# # Bail if the documents have not been updated to reflect the version to be built +# isPass = True +# dstPath = os.path.join(workPath, 'doc') +# for aDoc in ['ChangeLog.txt', 'ReadMe.txt']: +# srcPath = os.path.join(installPath, 'doc', aDoc) +# isPass &= checkDocVersion(srcPath, version) +# +# # Bail if any documents have versions that do not match. They should be updated. +# if isPass == False: +# errPrintln('Please update the documentation files before a release is built.') +# errPrintln('Build has been aborted.\n') +# exit(-1) + + # Auto update the AppInfo file with the version info + appInfoSrcFile = 'src/glum/app/AppInfo.java' + isVersionFound = False + for line in fileinput.FileInput(appInfoSrcFile, inplace=1): + if re.match('^\s*private\s*static\s*String\s*Version\s*=.*', line) != None: + line = '\tprivate static String Version = "' + version + '";\n' + isVersionFound = True + print(line, end='') + + if isVersionFound == False: + errPrintln('\nFailed to locate version declaration in library!') + errPrintln('\tVersion has NOT have been updated.') + errPrintln('\tSearched file: ' + appInfoSrcFile + '\n') + + + # Build the library jar + buildLibraryJar(version) + + # Confirm that we have built the class files properly by retrieving the + # version from the built application jar and comparing it to our version. + tmpVersion = getLibraryBinVersion(jarBinPathFN) + if tmpVersion != version: + print('Failure: Embedded release: {} Expected: {}\n\tAborting...'.format(tmpVersion, version)) + exit(-1) + + print(appBaseName + ' release ' + version + ' built. Assets:') + for aFilePath in [jarBinPathFN, jarSrcPathFN]: + print(' - ' + aFilePath) + print('') + + +def checkDocVersion(aFile, aVersion): + """Method that returns true if the version in the documentation file, aFile + matches the provided version. The documentation file should be a simple text + file where the version is recorded on the 2nd line. The 2nd line is expected + to match the following pattern: + Version: + + If the version does not match then the expected input vs the actual input + will be logged to stderr. This method is useful to allow a release to be + built and ensuring that the documentation files have been updated. + """ + verStr = 'Version: ' + aVersion + + workFileInput = fileinput.FileInput(aFile, mode = 'rb') + for line in workFileInput: + lineNum = workFileInput.lineno() + + # Convert from bytes to string. If there is an issue then just assume + # the empty string. + try: + lineStr = line.decode("utf-8") + lineStr = lineStr.rstrip() + except: + lineStr = '' + + if lineNum == 2 and lineStr != verStr: + errPrintln('Documentation file does not match version.') + errPrintln(' File: ' + aFile) + errPrintln(' [Line-2] Actual: ' + lineStr) + errPrintln(' [Line-2] Expected: ' + verStr) + errPrintln('') + return False + + return True + + +def errPrintln(aMessage=''): + """Print the specified string with a trailing newline to stderr. Each tab + character will be replaced with: 3 spaces""" + aMessage = aMessage.replace('\t', ' ') + sys.stderr.write(aMessage + '\n') + + +def getLibraryBinVersion(aJarBinPath): + """Method that will return the version of the specified binary jar file. + The binary jar file will be queried (package glum.app.AppGlum) and the + output will be parsed. Any failures will result in the abrupt exit of this + script.""" + try: + cpStr = aJarBinPath + exeCmd = [javaExe, '-cp', cpStr, 'glum.app.AppGlum', '--version'] + output = subprocess.check_output(exeCmd).decode('utf-8') + version = output.split(':')[-1].strip() + return version + except Exception as aExp: + traceback.print_exc() + exit(-1) + + +def getInstallRoot(): + """Returns the root path where the running script is installed.""" + argv = sys.argv; + installRoot = os.path.dirname(argv[0]) +# print('appInstallRoot: ' + appInstallRoot) + return installRoot + + +def handleSignal(signal, frame): + """Signal handler, typically used to capture ctrl-c.""" + print('User aborted processing!') + sys.exit(0) + + +if __name__ == "__main__": + # Logic to capture Ctrl-C and bail + signal.signal(signal.SIGINT, handleSignal) + + # Require python version 3.6 or later + targVer = (3, 6) + if sys.version_info < targVer: + print('The installed version of python is too old. Please upgrade.') + print(' Current version: ' + '.'.join(str(i) for i in sys.version_info)) + print(' Require version: ' + '.'.join(str(i) for i in targVer)) + sys.exit(-1) + + tmpDescr = 'Utility to build a ' + appBaseName + ' release\n' + parser = argparse.ArgumentParser(prefix_chars='-', description=tmpDescr, add_help=False, fromfile_prefix_chars='@') + parser.add_argument('--help', '-h', help='Show this help message and exit.', action='help') + parser.add_argument('--doNotClean', default=False, action='store_true', help='Do NOT remove temporary work folder created while generating release.') + parser.add_argument('--doFullBuild', default=False, action='store_true', help='Force a full build of the main jar file. (Unsupported action)') + parser.add_argument('--extraTag', default=None, action='store', help='Specify an extra tag for to the version.') + + # Intercept any request for a help message and bail + argv = sys.argv; + if '-h' in argv or '-help' in argv or '--help' in argv: + parser.print_help() + exit() + + # Parse the args + parser.formatter_class.max_help_position = 50 + args = parser.parse_args() + + # TODO: Finish this functionality + if args.doFullBuild == True: + print("Unsupported action: [--doFullBuild]. Skipping...") + + # Ensure required applications are installed + checkForInstalledApps() + + buildRelease(args.extraTag, args.doNotClean)