Initial import of metadata code moved from crucible-crust.

This commit is contained in:
James Peachey
2024-05-31 09:29:25 -04:00
commit 7d9a054bfe
64 changed files with 5613 additions and 0 deletions

21
.classpath Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry exported="true" kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-16">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/5"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

2
.factorypath Normal file
View File

@@ -0,0 +1,2 @@
<factorypath>
</factorypath>

1
.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
/target/

24
.project Normal file
View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>jsqrl</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.apache.ivyde.eclipse.ivynature</nature>
</natures>
</projectDescription>

View File

@@ -0,0 +1,4 @@
eclipse.preferences.version=1
encoding//Users/steelrj1/git_pristine/saavtk=UTF-8
encoding/<project>=UTF-8
encoding/src=UTF-8

View File

@@ -0,0 +1,513 @@
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.nonnull.secondary=
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
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.APILeak=warning
org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info
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=warning
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.nonnullTypeVariableFromLegacyInvocation=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.pessimisticNullAnalysisForFreeTypeVariables=warning
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.suppressWarningsNotFullyAnalysed=info
org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
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.unlikelyCollectionMethodArgumentType=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
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.unstableAutoModuleName=warning
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.unusedExceptionParameter=ignore
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_selector_in_method_invocation_on_expression_first_line=false
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_annotations_on_enum_constant=49
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0
org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=48
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0
org.eclipse.jdt.core.formatter.alignment_for_assertion_message=0
org.eclipse.jdt.core.formatter.alignment_for_assignment=16
org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16
org.eclipse.jdt.core.formatter.alignment_for_compact_if=0
org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=16
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_expressions_in_switch_case_with_arrow=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=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=0
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=0
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_permitted_types_in_type_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_record_components=0
org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=0
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
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=0
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=0
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0
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=0
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=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_statement_group_in_switch=0
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_record_constructor=next_line
org.eclipse.jdt.core.formatter.brace_position_for_record_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=true
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_between_different_tags=do not insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert
org.eclipse.jdt.core.formatter.comment.line_length=132
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_record_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=false
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=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=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_permitted_types=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=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_not_operator=do not 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_record_declaration=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_record_declaration=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_permitted_types=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=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_record_constructor=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_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_record_declaration=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=false
org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_if_empty
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_if_empty
org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_if_empty
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_if_empty
org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_if_empty
org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_if_empty
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_switch_body_block_on_one_line=one_line_never
org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never
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_if_empty
org.eclipse.jdt.core.formatter.lineSplit=132
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=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_record_declaration=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=space
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.text_block_indentation=0
org.eclipse.jdt.core.formatter.use_on_off_tags=false
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true
org.eclipse.jdt.core.formatter.wrap_before_assertion_message_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_before_switch_case_arrow_operator=false
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter

View File

@@ -0,0 +1,144 @@
eclipse.preferences.version=1
editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
formatter_profile=_SBMT
formatter_settings_version=23
sp_cleanup.add_all=false
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.array_with_curly=false
sp_cleanup.arrays_fill=false
sp_cleanup.bitwise_conditional_expression=false
sp_cleanup.boolean_literal=false
sp_cleanup.boolean_value_rather_than_comparison=false
sp_cleanup.break_loop=false
sp_cleanup.collection_cloning=false
sp_cleanup.comparing_on_criteria=false
sp_cleanup.comparison_statement=false
sp_cleanup.controlflow_merge=false
sp_cleanup.convert_functional_interfaces=false
sp_cleanup.convert_to_enhanced_for_loop=false
sp_cleanup.convert_to_enhanced_for_loop_if_loop_var_used=false
sp_cleanup.convert_to_switch_expressions=false
sp_cleanup.correct_indentation=false
sp_cleanup.do_while_rather_than_while=false
sp_cleanup.double_negation=false
sp_cleanup.else_if=false
sp_cleanup.embedded_if=false
sp_cleanup.evaluate_nullable=false
sp_cleanup.extract_increment=false
sp_cleanup.format_source_code=true
sp_cleanup.format_source_code_changes_only=false
sp_cleanup.hash=false
sp_cleanup.if_condition=false
sp_cleanup.insert_inferred_type_arguments=false
sp_cleanup.instanceof=false
sp_cleanup.instanceof_keyword=false
sp_cleanup.invert_equals=false
sp_cleanup.join=false
sp_cleanup.lazy_logical_operator=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.map_cloning=false
sp_cleanup.merge_conditional_blocks=false
sp_cleanup.multi_catch=false
sp_cleanup.never_use_blocks=false
sp_cleanup.never_use_parentheses_in_expressions=true
sp_cleanup.no_string_creation=false
sp_cleanup.no_super=false
sp_cleanup.number_suffix=false
sp_cleanup.objects_equals=false
sp_cleanup.on_save_use_additional_actions=false
sp_cleanup.one_if_rather_than_duplicate_blocks_that_fall_through=false
sp_cleanup.operand_factorization=false
sp_cleanup.organize_imports=true
sp_cleanup.overridden_assignment=false
sp_cleanup.overridden_assignment_move_decl=true
sp_cleanup.plain_replacement=false
sp_cleanup.precompile_regex=false
sp_cleanup.primitive_comparison=false
sp_cleanup.primitive_parsing=false
sp_cleanup.primitive_rather_than_wrapper=false
sp_cleanup.primitive_serialization=false
sp_cleanup.pull_out_if_from_if_else=false
sp_cleanup.pull_up_assignment=false
sp_cleanup.push_down_negation=false
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.reduce_indentation=false
sp_cleanup.redundant_comparator=false
sp_cleanup.redundant_falling_through_block_end=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=false
sp_cleanup.remove_trailing_whitespaces_all=true
sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
sp_cleanup.remove_unnecessary_array_creation=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_method_parameters=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.return_expression=false
sp_cleanup.simplify_lambda_expression_and_method_ref=false
sp_cleanup.single_used_field=false
sp_cleanup.sort_members=false
sp_cleanup.sort_members_all=false
sp_cleanup.standard_comparison=false
sp_cleanup.static_inner_class=false
sp_cleanup.strictly_equal_or_different=false
sp_cleanup.stringbuffer_to_stringbuilder=false
sp_cleanup.stringbuilder=false
sp_cleanup.stringbuilder_for_local_vars=true
sp_cleanup.stringconcat_to_textblock=false
sp_cleanup.substring=false
sp_cleanup.switch=false
sp_cleanup.system_property=false
sp_cleanup.system_property_boolean=false
sp_cleanup.system_property_file_encoding=false
sp_cleanup.system_property_file_separator=false
sp_cleanup.system_property_line_separator=false
sp_cleanup.system_property_path_separator=false
sp_cleanup.ternary_operator=false
sp_cleanup.try_with_resource=false
sp_cleanup.unlooped_while=false
sp_cleanup.unreachable_block=false
sp_cleanup.use_anonymous_class_creation=false
sp_cleanup.use_autoboxing=false
sp_cleanup.use_blocks=false
sp_cleanup.use_blocks_only_for_return_and_throw=false
sp_cleanup.use_directly_map_method=false
sp_cleanup.use_lambda=true
sp_cleanup.use_parentheses_in_expressions=false
sp_cleanup.use_string_is_blank=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
sp_cleanup.use_unboxing=false
sp_cleanup.use_var=false
sp_cleanup.useless_continue=false
sp_cleanup.useless_return=false
sp_cleanup.valueof_rather_than_instantiation=false

View File

@@ -0,0 +1,4 @@
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1

View File

@@ -0,0 +1,2 @@
eclipse.preferences.version=1
org.jboss.ide.eclipse.as.core.singledeployable.deployableList=

222
pom.xml Normal file
View File

@@ -0,0 +1,222 @@
<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>edu.jhuapl.ses.jsqrl</groupId>
<artifactId>jsqrl</artifactId>
<packaging>jar</packaging>
<version>0.0.1</version>
<name>jsqrl</name>
<description>VTK Support library</description>
<organization>
<name>Johns Hopkins University Applied Physics Lab</name>
<url>https://www.jhuapl.edu</url>
</organization>
<distributionManagement>
<repository>
<id>central</id>
<name>surfshop-releases</name>
<url>http://surfshop.jhuapl.edu:8081/artifactory/libs-release-local</url>
</repository>
<snapshotRepository>
<id>central</id>
<name>surfshop-snapshots</name>
<url>http://surfshop.jhuapl.edu:8081/artifactory/libs-snapshot-local</url>
</snapshotRepository>
</distributionManagement>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>16</maven.compiler.source>
<maven.compiler.target>16</maven.compiler.target>
<vtkVersion>1.0.0.25</vtkVersion>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
</dependencies>
<scm>
<connection>scm:git:http://hardin:8080/scm/git/vtk/saavtk</connection>
<developerConnection>scm:git:http://hardin:8080/scm/git/vtk/saavtk</developerConnection>
</scm>
<!-- Issue Management - TO BE UPDATED -->
<issueManagement>
<system>Quiver</system>
<url>https://quiver.jhuapl.edu/saavtk/issues</url>
</issueManagement>
<!-- Repositories, so can be built on checkout without settings.xml file in place -->
<repositories>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>thirdparty</id>
<name>libs-3rdparty-local</name>
<url>http://surfshop.jhuapl.edu:8081/artifactory/libs-3rdparty-local</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>libs-release</name>
<url>https://surfshop:8081/artifactory/libs-release</url>
</repository>
<repository>
<snapshots />
<id>snapshots</id>
<name>libs-snapshot</name>
<url>https://surfshop:8081/artifactory/libs-snapshot</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>OSGeo</id>
<name>release</name>
<url>https://repo.osgeo.org/repository/release/</url>
</repository>
<repository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central2</id>
<name>libs-release</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
<repository>
<snapshots />
<id>snapshots2</id>
<name>libs-snapshot</name>
<url>https://repo1.maven.org/maven2/</url>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central</id>
<name>libs-release</name>
<url>https://surfshop:8081/artifactory/libs-release</url>
</pluginRepository>
<pluginRepository>
<snapshots />
<id>snapshots</id>
<name>libs-snapshot</name>
<url>https://surfshop:8081/artifactory/libs-snapshot</url>
</pluginRepository>
<pluginRepository>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>central2</id>
<name>plugins-release</name>
<url>https://repo1.maven.org/maven2/</url>
</pluginRepository>
<pluginRepository>
<snapshots />
<id>snapshots2</id>
<name>plugins-snapshot</name>
<url>https://repo1.maven.org/maven2/</url>
</pluginRepository>
</pluginRepositories>
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>16</release>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<version>3.1.0</version>
<executions>
<!--
<execution>
<?m2e execute onConfiguration,onIncremental?>
<phase>compile</phase>
<configuration>
<target>
<mkdir dir="${project.build.directory}/classes/edu/jhuapl/ses/jsqrl/data"/>
<copy todir="${project.build.directory}/classes/edu/jhuapl/ses/jsqrl/data">
<fileset dir="src/edu/jhuapl/ses/jsqrl/data"/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
<execution>
<id>toolbar_icons</id>
<?m2e execute onConfiguration,onIncremental?>
<phase>compile</phase>
<configuration>
<target>
<mkdir dir="${project.build.directory}/classes/edu/jhuapl/jhuapl/ses/jsqrl/gui/render/toolbar" />
<copy todir="${project.build.directory}/classes/edu/jhuapl/ses/jsqrl/gui/render/toolbar">
<fileset dir="src/edu/jhuapl/ses/jsqrl/gui/render/toolbar/">
<include name="*.png"/>
</fileset>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
-->
<execution>
<id>icons</id>
<?m2e execute onConfiguration,onIncremental?>
<phase>compile</phase>
<configuration>
<target>
<mkdir dir="${project.build.directory}/classes/resources" />
<copy todir="${project.build.directory}/classes/resources">
<fileset dir="src/resources"/>
</copy>
</target>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,84 @@
package edu.jhuapl.ses.jsqrl.api;
import java.util.regex.Pattern;
/**
* @param <T> type of the object that may be associated with this key, used for compile-time safety
* only
*/
public class Key<T> implements Comparable<Key<?>> {
private static final Pattern NoLeadingWhiteSpace = Pattern.compile("^\\S.*", Pattern.DOTALL);
private static final Pattern NoTrailingWhiteSpace = Pattern.compile(".*\\S$", Pattern.DOTALL);
/**
* Return a key based on the supplied identification string.
*
* @param keyId the identification string of the key to be returned.
* @return the key
*
* @throws NullPointerException if argument is null
*/
public static <T> Key<T> of(String keyId) {
return new Key<>(keyId);
}
private final String keyId;
protected Key(String keyId) {
checkNotNull(keyId);
checkArgument(NoLeadingWhiteSpace.matcher(keyId).matches());
checkArgument(NoTrailingWhiteSpace.matcher(keyId).matches());
this.keyId = keyId;
}
public String getId() {
return keyId;
}
@Override
public final int compareTo(Key<?> that) {
if (that == null) {
return 1;
}
return this.getId().compareTo(that.getId());
}
@Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getId().hashCode();
return result;
}
@Override
public final boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof Key) {
Key<?> that = (Key<?>) other;
return this.getId().equals(that.getId());
}
return false;
}
@Override
public String toString() {
return keyId;
}
private static void checkNotNull(Object object) {
if (object == null) {
throw new NullPointerException();
}
}
private static void checkArgument(boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -0,0 +1,87 @@
package edu.jhuapl.ses.jsqrl.api;
import java.util.Collection;
/**
* Interface representing a heterogenous collection of objects that may be retrieved using keys,
* similar to {@link java.util.Map}, but with significant differences, mainly in how nulls and
* missing keys are treated, and in how type safety is (and is not) enforced.
*/
public interface Metadata {
/**
* Return the {@link Version} for the content of this collection of metadata (not different
* versions of the Metadata interface or associated abstractions).
*
* @return the content version object
*/
Version getVersion();
/**
* Get a collection of {@link Key}s contained in this metadata object. Implementors must guarantee
* this method remains consistent with other methods on the class, as described in the get(Key<?>
* key) method.
*
* @return the collection of keys
*/
Collection<Key<?>> getKeys();
/**
* Check whether this metadata object has the supplied key. Implementors must guarantee this
* method remains consistent with other methods on the class, as described in the get(Key<?> key)
* method.
*
* @param key the key to check
* @return true if this metadata object has a value associated with the provided key
* @throws NullPointerException if the provided key parameter is null
*/
boolean hasKey(Key<?> key);
/**
* Retrieve the value associated with the provided key, if that key-value pair is contained in
* this metadata object. Keys may not be null, but implementors are required to support null
* values associated with keys.
* <p>
*
* This method provides compile-time type safety only. Because of type erasure, it is possible to
* obtain a key that is present in the metadata, but which has the wrong parametrized type. This
* will lead to a ClassCastException when this method is called. It is the caller's responsibility
* to avoid and/or mitigate this contingency.
* <p>
*
* Implementions are required to be self-consistent in the following ways:
* <p>
*
* 1) For any metadata object, get(Key<?> key) must throw an IllegalArgumentException if and only
* if hasKey(Key<?> key) would return false for that same key parameter.
* <p>
*
* 2) For any metadata object, hasKey(Key<?> key) must return false if the key parameter would not
* be contained in the Collection returned by getKeys(). Implementations may choose whether to
* allow "private keys", i.e., keys not returned by getKeys, but for which hasKey returns true.
* <p>
*
* @param key whose associated value to retrieve
*
* @return the value (possibly null) associated with the key
*
* @throws NullPointerException if the provided key parameter is null
*
* @throws IllegalArgumentException if this metadata does not contain a key-value pairing
* associated with the key parameter
*
* @throws ClassCastException if the value associated with the key cannot be cast to the type
* provided by the key's type parameter
*/
<V> V get(Key<V> key);
/**
* Create a completely independent copy of this metadata object, i.e., a separate object whose
* methods would return identical results to the current object immediately after this method is
* called. Implementations are not required to return an object of the same type as the object on
* which this method is invoked.
*
* @return the copy of the metadata object
*/
Metadata copy();
}

View File

@@ -0,0 +1,26 @@
package edu.jhuapl.ses.jsqrl.api;
/**
* Abstraction representing a manager of {@link Metadata}, capable of storing/retrieving the content
* or state of one or more objects to/from a single Metadata object.
*/
public interface MetadataManager extends RepresentableAsMetadata {
/**
* Return a (complete and self-consistent) set of metadata derived from the content or state of
* one or more objects.
*
* @return destination object in which the metadata are stored
*/
@Override
Metadata store();
/**
* Retrieve a (complete and self-consistent) set of metadata in the provided source object. The
* metadata retrieved will typically be used to create or restore the state of one or more
* objects.
*
* @param source the source metadata object
*/
void retrieve(Metadata source);
}

View File

@@ -0,0 +1,28 @@
package edu.jhuapl.ses.jsqrl.api;
/**
* Functional interface whose method uses a supplied {@link Metadata} object to provide an object
* instance of a particular parametrized type &lt;T&gt. If &lt;T&gt is an instantiable class, this
* implies that the stored metadata must contain all information necessary to instantiate an object
* of type &lt;T&gt. For non-instantiable types (i.e. enumerations or other singletons), the
* metadata object need only contain enough information to identify the object to return.
* <p>
* In general if one provides an implementation of this interface, one provides also a complementary
* implementation of {@link ProvidesMetadataFromGenericObject} interface and registers both of these
* implementations with an {@InstanceGetter}.
*
* @param <T> the object type that can be provided from suitable Metadata
* @see {@link ProvidesMetadataFromGenericObject}
*/
public interface ProvidesGenericObjectFromMetadata<T> {
/**
* Use the supplied {@link Metadata} to create (or get) an object of the appropriate instance
* type.
*
* @param metadata the metadata to use to provide the instance
* @return an instance of the object of the parametrized type T obtained based on the metadata
*/
T provide(Metadata metadata);
}

View File

@@ -0,0 +1,25 @@
package edu.jhuapl.ses.jsqrl.api;
/**
* Functional interface whose method uses a supplied object of the parametrized type &lt;T&gt; to
* provide a {@link Metadata} object that encapsulates the identity and/or state of the original
* object. An implementation of this interface is typically paired with an implementation of
* {@link ProvidesGenericObjectFromMetadata}, an implementation of this interface may be used to
* store all information needed to instantiate on object of type &lt;T&gt;.
*
* @param <T> the object type for which Metadata can be provided.
* @see {@link ProvidesGenericObjectFromMetadata}
*/
public interface ProvidesMetadataFromGenericObject<T> {
/**
* Use the supplied object to create a {@link Metadata} object that encapsulates the state of the
* original object.
*
* @param object the object whose state will be encapsulated in Metadata.
* @return the Metadata.
* @see {@link ProvidesGenericObjectFromMetadata}
*/
Metadata provide(T object);
}

View File

@@ -0,0 +1,19 @@
package edu.jhuapl.ses.jsqrl.api;
/**
* Abstraction that is capable of representing (storing) the content or state of one or more objects
* as {@link Metadata}.
*
* Implementations may provide representations for themselves, or on behalf of other objects.
*
* @see {@link MetadataManager}
*/
public interface RepresentableAsMetadata {
/**
* Return a set of {@link Metadata} that represents the content or state of one or more objects.
*
* @return the metadata representing the object of the parameterized type
*/
Metadata store();
}

View File

@@ -0,0 +1,36 @@
package edu.jhuapl.ses.jsqrl.api;
import java.io.File;
import java.io.IOException;
public interface Serializer {
/**
* Return the version of the serializer/stored file format itself.
*
* @return the version.
*/
Version getVersion();
/**
* Register the provided manager to manage Metadata objects associated with the provided key.
*
* Managers are called in the order in which they were originally added. Call this method the very
* first time a method is invoked that uses a Metadata object to save and/or restore the state of
* an object. This may or may not be within a constructor.
*
* This is so that program execution will more-or-less preserve the natural order of operations
* affecting objects whose metadata is serialized/deserialized.
*
* @param key the key identifying the Metadata objects this manager manages
* @param manager the manager for Metadata objects associated with the key
* @throws IllegalStateException if method is called more than once with the same key
*/
void register(Key<? extends Metadata> key, MetadataManager manager);
void deregister(Key<? extends Metadata> key);
void load(File file) throws IOException;
void save(File file) throws IOException;
}

View File

@@ -0,0 +1,6 @@
package edu.jhuapl.ses.jsqrl.api;
public class Spud
{
}

View File

@@ -0,0 +1,24 @@
package edu.jhuapl.ses.jsqrl.api;
/**
* A {@link RepresentableAsMetadata} that is restricted to representing a single object of one
* particular type. This includes a {@link Key} providing a unique identifier of the type of object
* that may be converted to {@link Metadata}.
*
* The existence of the Key identifier makes implementations suitable for serialization of objects
* of the parametrized type.
*
* @param <T> the object type that can be converted to Metadata
* @see {@link ProvidesMetadataFromGenericObject}
*/
public interface StorableAsMetadata<T> extends RepresentableAsMetadata {
/**
* Return a key that uniquely identifies the type of object being stored. This is used in lieu of
* the object's Class to identify the type for purposes of saving (serializing) the
* {@link Metadata}.
*
* @return the key
*/
Key<? extends T> getKey();
}

View File

@@ -0,0 +1,88 @@
package edu.jhuapl.ses.jsqrl.api;
public final class Version implements Comparable<Version> {
// Release notes are now in the package-info file.
public static Version of(int major, int minor) {
return new Version(major, minor);
}
public static Version of(String versionString) {
checkNotNull(versionString);
checkArgument(versionString.matches("^\\d+\\.\\d+$"));
String[] versionInfo = versionString.split("\\.", 2);
if (versionInfo.length != 2) {
throw new IllegalArgumentException();
}
return of(Integer.parseInt(versionInfo[0]), Integer.parseInt(versionInfo[1]));
}
private final int major;
private final int minor;
private Version(int major, int minor) {
checkArgument(major >= 0 && minor >= 0);
this.major = major;
this.minor = minor;
}
public int getMajor() {
return major;
}
public int getMinor() {
return minor;
}
@Override
public int compareTo(Version that) {
int result = 1;
if (that != null) {
result = Integer.compare(this.major, that.major);
if (result == 0) {
result = Integer.compare(this.minor, that.minor);
}
}
return result;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + major;
result = prime * result + minor;
return result;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Version)) {
return false;
}
Version that = (Version) other;
return (this.major == that.major && this.minor == that.minor);
}
@Override
public String toString() {
return major + "." + minor;
}
private static void checkNotNull(Object object) {
if (object == null) {
throw new NullPointerException();
}
}
private static void checkArgument(boolean expression) {
if (!expression) {
throw new IllegalArgumentException();
}
}
}

View File

@@ -0,0 +1,150 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.lang.reflect.Array;
import java.util.Map.Entry;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
/**
* Base implementation that assumes/requires all metadata keys to be stored in a standard
* {@link java.util.Map}. Implementations are provided for all {@link Metadata} methods except copy.
* An additional protected abtract method, getMap() is provided so that subclasses may provide the
* map used by this impementation. To ensure invariants are preserved in sublasses, all methods that
* rely on this implementation's contract are final.
*/
public abstract class AbstractMetadata implements Metadata {
/**
* Object used to represent null. By proxying null with this object, it is possible to use any
* {@link java.util.Map} implementation for key-value pairs.
*/
private static final Object NULL_OBJECT = new Object() {
@Override
public String toString() {
// Deliberately capitalizing this so that the astute debugger has a chance of
// noticing that this object is not actually a null pointer.
return "NULL";
}
};
private final Version version;
protected AbstractMetadata(Version version) {
Preconditions.checkNotNull(version);
this.version = version;
}
/**
* Provide an immutable copy of the map of keys to values. Because the returned map does not
* support nulls for keys or values, implementations must use the object returned by getNullObject
* to represent all null values.
*
* @return the map of keys to values
*/
public abstract ImmutableMap<Key<?>, Object> getMap();
@Override
public final Version getVersion() {
return version;
}
@Override
public final boolean hasKey(Key<?> key) {
Preconditions.checkNotNull(key);
return getMap().containsKey(key);
}
@Override
public final <V> V get(Key<V> key) {
Preconditions.checkNotNull(key);
Object object = getMap().get(key);
if (object == null) {
throw new IllegalArgumentException("FixedMetadata does not contain key " + key);
}
if (getNullObject() == object) {
return null;
}
@SuppressWarnings("unchecked")
V result = (V) object;
return result;
}
@Override
public abstract AbstractMetadata copy();
@Override
public final int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + getVersion().hashCode();
result = prime * result + getMap().hashCode();
return result;
}
@Override
public final boolean equals(Object other) {
if (this == other) {
return true;
}
if (other instanceof AbstractMetadata) {
AbstractMetadata that = (AbstractMetadata) other;
if (!this.getVersion().equals(that.getVersion())) {
return false;
}
ImmutableMap<Key<?>, Object> thisMap = this.getMap();
ImmutableMap<Key<?>, Object> thatMap = that.getMap();
for (Entry<Key<?>, Object> entry : thisMap.entrySet()) {
Key<?> key = entry.getKey();
if (!thatMap.containsKey(key)) {
return false;
}
Object thisValue = entry.getValue();
Object thatValue = thatMap.get(key);
try {
int thisLength = Array.getLength(thisValue);
int thatLength = Array.getLength(thatValue);
if (thisLength != thatLength) {
return false;
}
for (int index = 0; index < thisLength; ++index) {
if (!Array.get(thisValue, index).equals(Array.get(thatValue, index))) {
return false;
}
}
} catch (@SuppressWarnings("unused") IllegalArgumentException e) {
// One or both items are not arrays, so just fall back on native equals method.
if (!thisValue.equals(thatValue)) {
return false;
}
}
}
return true;
}
return false;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder("(Metadata) version ");
builder.append(getVersion());
for (Key<?> key : getKeys()) {
builder.append("\n");
builder.append(key + " = " + get(key));
}
return builder.toString();
}
protected static Object getNullObject() {
return NULL_OBJECT;
}
}

View File

@@ -0,0 +1,23 @@
package edu.jhuapl.ses.jsqrl.impl;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
public abstract class AbstractMetadataManager implements MetadataManager {
private final Version version;
protected AbstractMetadataManager(int major, int minor) {
this(Version.of(major, minor));
}
protected AbstractMetadataManager(Version version) {
this.version = version;
}
protected SettableMetadata createMetadata() {
return SettableMetadata.of(version);
}
}

View File

@@ -0,0 +1,46 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.util.Collection;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
public final class EmptyMetadata implements Metadata {
private static final EmptyMetadata INSTANCE = new EmptyMetadata();
public static EmptyMetadata instance() {
return INSTANCE;
}
@Override
public Version getVersion() {
throw new UnsupportedOperationException("Empty metadata has no version");
}
@Override
public Collection<Key<?>> getKeys() {
throw new UnsupportedOperationException("Empty metadata has no keys");
}
@Override
public boolean hasKey(Key<?> key) {
throw new UnsupportedOperationException("Empty metadata does not have key " + key);
}
@Override
public <V> V get(Key<V> key) {
throw new UnsupportedOperationException("Empty metadata has no value for key " + key);
}
@Override
public Metadata copy() {
return this;
}
@Override
public String toString() {
return "Empty metadata";
}
}

View File

@@ -0,0 +1,59 @@
package edu.jhuapl.ses.jsqrl.impl;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.AbstractMetadata;
public class FixedMetadata extends AbstractMetadata {
public static FixedMetadata of(Metadata metadata) {
Preconditions.checkNotNull(metadata);
if (metadata instanceof FixedMetadata) {
return (FixedMetadata) metadata;
}
Version version = metadata.getVersion();
ImmutableList<Key<?>> keys = ImmutableList.copyOf(metadata.getKeys());
ImmutableMap.Builder<Key<?>, Object> builder = ImmutableMap.builder();
for (Key<?> key : keys) {
Object object = metadata.get(key);
if (object == null) {
object = getNullObject();
}
builder.put(key, object);
}
return new FixedMetadata(version, keys, builder.build());
}
private final ImmutableList<Key<?>> keys;
private final ImmutableMap<Key<?>, Object> map;
protected FixedMetadata(Version version, ImmutableList<Key<?>> keys,
ImmutableMap<Key<?>, Object> map) {
super(version);
this.keys = keys;
this.map = map;
}
@Override
public ImmutableList<Key<?>> getKeys() {
return keys;
}
@Override
public FixedMetadata copy() {
return this;
}
@Override
public ImmutableMap<Key<?>, Object> getMap() {
return map;
}
}

View File

@@ -0,0 +1,260 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.common.base.Preconditions;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.ProvidesGenericObjectFromMetadata;
import edu.jhuapl.ses.jsqrl.api.ProvidesMetadataFromGenericObject;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
/**
* A collection of {@link ProvidesGenericObjectFromMetadata}s that may be stored or retrieved using
* an associated {@link Key}. Keys must be unique within one InstanceGetter.
*
* @param <T> the object type that can be gotten from the Metadata
*/
public final class InstanceGetter {
/**
* Return a reference to the standard/global InstanceGetter. Most applications will use this so
* that any code may gain access to the various types of available
* {@link ProvidesGenericObjectFromMetadata} objects.
*
* @return the instance
*/
public static InstanceGetter defaultInstanceGetter() {
return DEFAULT_INSTANCE_GETTER;
}
private static final InstanceGetter DEFAULT_INSTANCE_GETTER = new InstanceGetter();
private final SortedMap<Key<?>, ProvidesGenericObjectFromMetadata<?>> fromMetadataMap;
private final Map<Class<?>, ProvidesMetadataFromGenericObject<?>> toMetadataMap;
private final BiMap<Class<?>, Key<?>> keyMap;
private final List<Class<?>> abstractTypes;
private final List<Class<?>> interfaceTypes;
/**
* Create a new InstanceGetter. In general, it's best to use the standard/global InstanceGetter
* supplied by the defaultInstanceGetter method. However, this constructor is public in case it
* ever becomes necessary to stand up an independent one (say to override how objects of a
* particular class are proxied by default).
*/
public InstanceGetter() {
this.fromMetadataMap = new TreeMap<>();
this.toMetadataMap = new HashMap<>();
this.keyMap = HashBiMap.create();
this.abstractTypes = new ArrayList<>();
this.interfaceTypes = new ArrayList<>();
}
public boolean isProvidableFromMetadata(Key<?> proxyTypeKey) {
Preconditions.checkNotNull(proxyTypeKey);
return fromMetadataMap.containsKey(proxyTypeKey);
}
/**
* Get the {@link ProvidesGenericObjectFromMetadata} object that matches the supplied key,
* provided the InstanceGetter has access to one.
*
* @param proxyTypeKey the key uniquely identifying the type of the object to be handled by the
* returned {@link ProvidesGenericObjectFromMetadata}
* @return the helper object that may be used to create/get objects from {@link Metadata}
* @throws IllegalArgumentException if this InstanceGetter does not have a
* {@link ProvidesGenericObjectFromMetadata} object for the supplied key
* @throws ClassCastException if the supplied key matches a
* {@link ProvidesGenericObjectFromMetadata} object managed by this InstanceGetter, but
* the key has the wrong type.
*/
public <T> ProvidesGenericObjectFromMetadata<T> providesGenericObjectFromMetadata(
Key<T> proxyTypeKey) {
Preconditions.checkNotNull(proxyTypeKey);
Preconditions.checkArgument(fromMetadataMap.containsKey(proxyTypeKey),
"Unable to provide proxied object from metadata for type " + proxyTypeKey);
@SuppressWarnings("unchecked")
ProvidesGenericObjectFromMetadata<T> result =
(ProvidesGenericObjectFromMetadata<T>) fromMetadataMap.get(proxyTypeKey);
return result;
}
public boolean isStorableAsMetadata(Object object) {
if (object == null) {
return false;
}
return isTypeStorableAsMetadata(object.getClass());
}
public boolean isTypeStorableAsMetadata(Class<?> type) {
return findBestMatchForType(type) != null;
}
public <T> Key<T> getKeyForType(Class<?> objectType) {
Preconditions.checkNotNull(objectType);
Class<?> matchingType = findBestMatchForType(objectType);
if (matchingType == null) {
throw new IllegalArgumentException();
}
@SuppressWarnings("unchecked")
Key<T> result = (Key<T>) keyMap.get(matchingType);
return result;
}
public <T> Class<T> getTypeForKey(Key<?> typeKey) {
Preconditions.checkNotNull(typeKey);
BiMap<Key<?>, Class<?>> typeMap = keyMap.inverse();
Preconditions.checkArgument(typeMap.containsKey(typeKey));
@SuppressWarnings("unchecked")
Class<T> result = (Class<T>) typeMap.get(typeKey);
return result;
}
public <T> ProvidesMetadataFromGenericObject<T> providesMetadataFromGenericObject(
Class<?> objectType) {
Preconditions.checkNotNull(objectType);
Class<?> matchingType = findBestMatchForType(objectType);
Preconditions.checkArgument(matchingType != null);
@SuppressWarnings("unchecked")
ProvidesMetadataFromGenericObject<T> result =
(ProvidesMetadataFromGenericObject<T>) toMetadataMap.get(matchingType);
return result;
}
/**
* Call the 4-argument register method instead of this one.
*
* @param proxyTypeKey
* @param fromMetadata
*/
@Deprecated
public <T> void register(Key<T> proxyTypeKey,
ProvidesGenericObjectFromMetadata<? extends T> fromMetadata) {
Preconditions.checkNotNull(proxyTypeKey);
Preconditions.checkNotNull(fromMetadata);
Preconditions.checkState(!fromMetadataMap.containsKey(proxyTypeKey),
"Cannot register metadata proxy more than once for type " + proxyTypeKey);
fromMetadataMap.put(proxyTypeKey, fromMetadata);
}
/**
* Register proxy metadata store and retrieve objects to work with the supplied key and type.
*
* @param proxyTypeKey the key identifying the type of object that may be obtained by the
* {@link ProvidesGenericObjectFromMetadata}. This is encoded in the stored metadata.
* @param fromMetadata the {@link ProvidesGenericObjectFromMetadata} object to associate with this
* key and value type. This object is used to supply an instance when deserializing.
* @param objectType the specific type of object that may be stored and retrieved to/from
* metadata.
* @param toMetadata the {@link ProvidesMetadataFromGenericObject} object to associate with this
* key and value type. This object is used to encode an instance of the object as metadata.
* @throws IllegalStateException if this InstanceGetter already was called with the supplied
* proxyTypeKey or objectType.
*/
public <T> void register(Key<T> proxyTypeKey,
ProvidesGenericObjectFromMetadata<? extends T> fromMetadata, Class<?> objectType,
ProvidesMetadataFromGenericObject<? extends T> toMetadata) {
Preconditions.checkNotNull(proxyTypeKey);
Preconditions.checkNotNull(fromMetadata);
Preconditions.checkNotNull(objectType);
Preconditions.checkNotNull(toMetadata);
Preconditions.checkState(!fromMetadataMap.containsKey(proxyTypeKey),
"Cannot register metadata proxy more than once for type " + proxyTypeKey);
Preconditions.checkState(!toMetadataMap.containsKey(objectType),
"Cannot register metadata proxies more than once for object type "
+ Utilities.simpleName(objectType));
Preconditions.checkState(!keyMap.containsKey(objectType),
"Cannot register more than one proxy key for an object type");
Preconditions.checkState(!keyMap.inverse().containsKey(proxyTypeKey),
"Cannot register more than one object type with the same proxy key");
fromMetadataMap.put(proxyTypeKey, fromMetadata);
toMetadataMap.put(objectType, toMetadata);
keyMap.put(objectType, proxyTypeKey);
if ((objectType.getModifiers() & Modifier.ABSTRACT) != 0) {
abstractTypes.add(objectType);
}
if (objectType.isInterface()) {
interfaceTypes.add(objectType);
}
}
/**
* Deregister (remove/don't track or use) the {@link ProvidesGenericObjectFromMetadata} associated
* with this key, if any.
*
* @param proxyTypeKey the key identifying the MetadataToObject to remove
*/
public void deRegister(Key<?> proxyTypeKey) {
Preconditions.checkNotNull(proxyTypeKey);
fromMetadataMap.remove(proxyTypeKey);
Class<?> objectType = keyMap.inverse().get(proxyTypeKey);
if (objectType != null) {
toMetadataMap.remove(objectType);
keyMap.remove(objectType);
abstractTypes.remove(objectType);
interfaceTypes.remove(objectType);
}
}
protected Class<?> findBestMatchForType(Class<?> objectType) {
Preconditions.checkNotNull(objectType);
Class<?> result = null;
if (keyMap.containsKey(objectType)) {
result = objectType;
}
if (result == null) {
// Search for match to a superclass.
for (Class<?> abstractType : abstractTypes) {
if (abstractType.isAssignableFrom(objectType)) {
result = abstractType;
break;
}
}
}
if (result == null) {
// Search for first match to an interface.
for (Class<?> interfaceType : interfaceTypes) {
if (interfaceType.isAssignableFrom(objectType)) {
result = interfaceType;
break;
}
}
}
return result;
}
}

View File

@@ -0,0 +1,63 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
public class MetadataManagerCollection {
public static MetadataManagerCollection of() {
return new MetadataManagerCollection();
}
private final List<Key<? extends Metadata>> keysInOrder;
private final SortedMap<Key<? extends Metadata>, MetadataManager> managers;
private MetadataManagerCollection() {
this.keysInOrder = new ArrayList<>();
this.managers = new TreeMap<>();
}
/**
* Add a manager to this collection of managers. The manager will be associated with Metadata
* identified by the supplied key.
*
* @param key the key to Metadata objects this manager will manage
* @param manager the manager
* @throws IllegalStateException if there is already a manager associated with the supplied key
* @throws NullPointerException if any argument is null
*/
public void add(Key<? extends Metadata> key, MetadataManager manager) {
Preconditions.checkNotNull(key);
Preconditions.checkNotNull(manager);
Preconditions.checkState(!managers.containsKey(key));
keysInOrder.add(key);
managers.put(key, manager);
}
public void remove(Key<? extends Metadata> key) {
Preconditions.checkNotNull(key);
keysInOrder.remove(key);
managers.remove(key);
}
public ImmutableList<Key<? extends Metadata>> getKeys() {
return ImmutableList.copyOf(keysInOrder);
}
public MetadataManager getManager(Key<? extends Metadata> key) {
Preconditions.checkNotNull(key);
Preconditions.checkArgument(managers.containsKey(key));
return managers.get(key);
}
}

View File

@@ -0,0 +1,181 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;
import java.util.Objects;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.google.common.base.Preconditions;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
/**
* Adaptor to serialize/deserialize objects that do not explicitly cooperate with the metadata
* framework, but that *do* implement {@link Serializable}. This adaptor uses byte array objects and
* gzipping/gunzipping streams to serialize an object to a string, then saves that string using the
* metadata framework. This adaptor can also do the reverse and create an object from a string that
* holds the object's serialized form.
* <p>
* The purpose of this class is to make it easier to support hierarchies and complex objects with
* the metadata framework without writing explicit instance-getting code. The trade-off is that the
* strings written are long and not human readable, whereas writing instance-getting code for each
* type allows thec caller to control how objects are written and read.
*
* @author James Peachey
*
* @param <S> the type of item, which must extend {@link Serializable}
*/
public final class SerializableItem<S extends Serializable> {
/**
* Boilerplate metadata stuff.
*/
private static final Version MetadataVersion = Version.of(1, 0);
private static final Key<Class<Serializable>> ItemTypeKey = Key.of("itemType");
private static final Key<String> EncodedStringKey = Key.of("encodedString");
static {
/**
* Add to the metadata framework the necessary code to handle encoding/decoding the item.
*/
InstanceGetter.defaultInstanceGetter().register(Key.of("SerializableItem"), //
metadata -> {
Class<Serializable> itemType = metadata.get(ItemTypeKey);
String encodedString = metadata.get(EncodedStringKey);
Serializable serializable;
try {
serializable = decodeFromString(itemType, encodedString);
} catch (Exception e) {
serializable = null;
}
return new SerializableItem<Serializable>(itemType, serializable);
}, SerializableItem.class, settings -> {
SettableMetadata metadata = SettableMetadata.of(MetadataVersion);
metadata.put(ItemTypeKey, settings.getItemType());
String encodedString;
try {
encodedString = encodeToString(settings.getItem());
} catch (Exception e) {
encodedString = null;
}
metadata.put(EncodedStringKey, encodedString);
return metadata;
});
}
/**
* Empty static method that may be called to ensure static initialization blocks have executed.
*/
public static void init() {
}
/**
* Decode an item that is encoded in the specified string and cast it to the specified item type.
*
* @param <S> generic type, which must extend {@link Serializable}
* @param itemType type of item being decoded
* @param encodedString the string
* @return the decoded item
* @throws IOException if the gunzipping/streaming operation(s) throw one
* @throws ClassNotFoundException if the class of the object type being decoded cannot be found
*/
public static <S extends Serializable> S decodeFromString(Class<S> itemType, String encodedString)
throws IOException, ClassNotFoundException {
Preconditions.checkNotNull(itemType);
if (encodedString == null) {
return null;
}
byte[] data = Base64.getDecoder().decode(encodedString);
try (ObjectInputStream ois =
new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(data)))) {
return itemType.cast(ois.readObject());
}
}
/**
* Encode the specified {@link Serializable} item into a string.
*
* @param item the item to encode
* @return the encoded string
* @throws IOException if the gzipping/streaming operation(s) throw one
*/
public static String encodeToString(Serializable item) throws IOException {
if (item == null || item instanceof String) {
return (String) item;
}
String encodedString = null;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos =
new ObjectOutputStream(/* new GZIPOutputStream( */new GZIPOutputStream(baos))) {
oos.writeObject(item);
}
encodedString = Base64.getEncoder().encodeToString(baos.toByteArray());
}
return encodedString;
}
private final Class<S> itemType;
private final S item;
public SerializableItem(Class<S> itemType, S item) {
this.itemType = itemType;
this.item = item;
}
/**
* @return the concrete type of the serializable item
*/
public Class<S> getItemType() {
return itemType;
}
/**
* @return the item that can be serialized
*/
public S getItem() {
return item;
}
@Override
public int hashCode() {
return Objects.hash(item, itemType);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SerializableItem)) {
return false;
}
SerializableItem<?> other = (SerializableItem<?>) obj;
return Objects.equals(item, other.item) && Objects.equals(itemType, other.itemType);
}
@Override
public String toString() {
return item != null ? item.toString() : null;
}
}

View File

@@ -0,0 +1,178 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.AbstractMetadata;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
public class SettableMetadata extends AbstractMetadata {
public static SettableMetadata of(Version version) {
return new SettableMetadata(version, new ArrayList<>(), new HashMap<>());
}
public static SettableMetadata of(Metadata metadata) {
Preconditions.checkNotNull(metadata);
SettableMetadata result = SettableMetadata.of(metadata.getVersion());
for (Key<?> key : metadata.getKeys()) {
@SuppressWarnings("unchecked")
Key<Object> objectKey = (Key<Object>) key;
result.put(objectKey, metadata.get(key));
}
return result;
}
private final List<Key<?>> keys;
private final Map<Key<?>, Object> map;
protected SettableMetadata(Version version, List<Key<?>> keys, Map<Key<?>, Object> map) {
super(version);
Preconditions.checkNotNull(keys);
Preconditions.checkNotNull(map);
this.keys = keys;
this.map = map;
}
@Override
public ImmutableList<Key<?>> getKeys() {
return ImmutableList.copyOf(keys);
}
@Override
public SettableMetadata copy() {
return new SettableMetadata(getVersion(), new ArrayList<>(keys), new HashMap<>(map));
}
@Override
public ImmutableMap<Key<?>, Object> getMap() {
return ImmutableMap.copyOf(map);
}
public final <V> SettableMetadata put(Key<V> key, V value) {
Preconditions.checkNotNull(key);
Class<?> storedAsType = checkStorable(value);
if (!hasKey(key)) {
keys.add(key);
}
if (value == null) {
map.put(key, getNullObject());
} else {
map.put(key, copyOrUse(storedAsType, value));
}
return this;
}
public void clear() {
keys.clear();
map.clear();
}
protected static void validateIterable(Iterable<?> iterable) {
for (Object item : iterable) {
checkStorable(item);
}
}
protected static void validateMap(Map<?, ?> map) {
Class<?> mapKeyType = null;
for (Entry<?, ?> entry : map.entrySet()) {
Class<?> keyType = checkStorable(entry.getKey());
if (mapKeyType == null) {
mapKeyType = keyType;
} else if (keyType != null && keyType != mapKeyType) {
throw new IllegalArgumentException(
"Cannot put a key of type " + Utilities.simpleName(keyType)
+ " in a map using keys of type " + Utilities.simpleName(mapKeyType));
}
checkStorable(entry.getValue());
}
}
@SuppressWarnings("unchecked")
protected static <V> V copyOrUse(Class<?> storedAsType, V value) {
if (SortedMap.class.isAssignableFrom(storedAsType)) {
value = (V) new TreeMap<>((SortedMap<?, ?>) value);
} else if (Map.class.isAssignableFrom(storedAsType)) {
value = (V) new LinkedHashMap<>((Map<?, ?>) value);
} else if (List.class.isAssignableFrom(storedAsType)) {
value = (V) new ArrayList<>((List<?>) value);
} else if (SortedSet.class.isAssignableFrom(storedAsType)) {
value = (V) new TreeSet<>((SortedSet<?>) value);
} else if (Set.class.isAssignableFrom(storedAsType)) {
value = (V) new LinkedHashSet<>((Set<?>) value);
}
return value;
}
/**
* Check whether the specified object may be represented as metadata. If it can, the type used by
* the metadata system to represent the object is returned. If the object cannot be represented by
* the metadata system, an {@link IllegalArgumentException} is thrown. This method never returns
* null.
* <p>
* This method uses the {@link Utilities#classifyStorableType(Class)} to determine whether the
* object is storable using the primary metadata mechanism. Unlike that method, however, if the
* object is not storable in the primary way, but the item's class implements
* {@link Serializable}, this method returns Serializable.class, because the item will be
* successfully stored/retrieved using the secondary mechanism for storing Serializable items, in
* which objects are serialized as Strings.
*
* @param object the item to be stored as metadata
* @return the type used to represent this item
*/
protected static Class<?> checkStorable(Object object) {
if (object == null) {
return null;
}
if (object instanceof List) {
validateIterable((Iterable<?>) object);
return List.class;
}
if (object instanceof SortedMap) {
validateMap((Map<?, ?>) object);
return SortedMap.class;
}
if (object instanceof Map) {
validateMap((Map<?, ?>) object);
return Map.class;
}
if (object instanceof SortedSet) {
validateIterable((Iterable<?>) object);
return SortedSet.class;
}
if (object instanceof Set) {
validateIterable((Iterable<?>) object);
return Set.class;
}
Class<?> type = Utilities.classifyStorableType(object.getClass());
if (type == null && Serializable.class.isAssignableFrom(object.getClass())) {
type = Serializable.class;
}
if (type == null) {
throw new IllegalArgumentException("Cannot directly represent objects of type "
+ Utilities.simpleName(object.getClass()) + " as metadata");
}
return type;
}
}

View File

@@ -0,0 +1,99 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.util.SortedSet;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
import edu.jhuapl.ses.jsqrl.api.RepresentableAsMetadata;
import edu.jhuapl.ses.jsqrl.api.Serializer;
import edu.jhuapl.ses.jsqrl.impl.gson.Serializers;
/**
* This is a helper implementation designed to wrap another implementation of the
* {@link MetadataManager} interface.
*/
public final class TrackedMetadataManager implements MetadataManager, RepresentableAsMetadata {
public static TrackedMetadataManager of(String metadataId) {
return new TrackedMetadataManager(metadataId, Serializers.getDefault());
}
private static final SortedSet<String> MANAGER_IDENTIFIERS = new TreeSet<>();
private final Key<Metadata> metadataKey;
private final Serializer serializer;
private MetadataManager manager;
/**
* Construct a new manager that will use the provided serializer. The identifying string provided
* must be unique within the currently running application. Note that the new manager is not added
* to the serializer by this constructor. That will be done when another manager is registered
* with this manager.
*
* @param metadataId string used to idenfity this manager within the serializer
* @param serializer the serializer
* @throws IllegalStateException if the serializer already has a manager associated with the
* provided string
* @throws NullPointerException if any argument is null
*/
private TrackedMetadataManager(String metadataId, Serializer serializer) {
Preconditions.checkNotNull(metadataId);
Preconditions.checkNotNull(serializer);
Preconditions.checkState(!MANAGER_IDENTIFIERS.contains(metadataId),
"Duplicated manager identifier " + metadataId);
MANAGER_IDENTIFIERS.add(metadataId);
this.metadataKey = Key.of(metadataId);
this.serializer = serializer;
this.manager = null;
}
/**
* Perform all initializations required prior to serializing/deserializing metadata managed by
* this manager. Note that all of this implementation's functions as a manager work just fine
* without calling this method. Moreover, it is safe to call this method multiple times.
*
* A separate initialize method is provided so that source objects may be serialized in the
* natural order established by the runtime function of the tool, rather than the order in which
* source objects were instantiated, which may not be the same.
*
* @throws IllegalStateException if a manager was already registered, or if the serializer fails
* to register this manager cleanly.
*/
public void register(MetadataManager manager) {
Preconditions.checkNotNull(manager);
Preconditions.checkState(this.manager == null);
serializer.register(metadataKey, this);
this.manager = manager;
}
public boolean isRegistered() {
return manager != null;
}
public Key<Metadata> getKey() {
return metadataKey;
}
/**
* {@inheritDoc}
*/
@Override
public final Metadata store() {
Preconditions.checkState(manager != null);
return manager.store();
}
/**
* {@inheritDoc}
*/
@Override
public final void retrieve(Metadata sourceMetadata) {
Preconditions.checkNotNull(sourceMetadata);
Preconditions.checkState(manager != null);
manager.retrieve(sourceMetadata);
}
}

View File

@@ -0,0 +1,232 @@
package edu.jhuapl.ses.jsqrl.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
import edu.jhuapl.ses.jsqrl.api.ProvidesMetadataFromGenericObject;
import edu.jhuapl.ses.jsqrl.api.StorableAsMetadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
public class Utilities {
// Release notes are now in the package-info file.
public static <K> ImmutableMap<K, Metadata> bulkStore(Map<K, MetadataManager> managers) {
Preconditions.checkNotNull(managers);
ImmutableMap.Builder<K, Metadata> builder = ImmutableMap.builder();
for (Entry<K, MetadataManager> entry : managers.entrySet()) {
builder.put(entry.getKey(), entry.getValue().store());
}
return builder.build();
}
public static <K> void bulkRetrieve(Map<K, MetadataManager> managers, Map<K, Metadata> metadata) {
Preconditions.checkNotNull(managers);
Preconditions.checkNotNull(metadata);
for (Entry<K, MetadataManager> entry : managers.entrySet()) {
K key = entry.getKey();
if (metadata.containsKey(key)) {
entry.getValue().retrieve(metadata.get(key));
}
}
}
public static Key<?> provideTypeKeyIfPossible(Object object) {
InstanceGetter instanceGetter = InstanceGetter.defaultInstanceGetter();
Key<?> key = null;
if (instanceGetter.isStorableAsMetadata(object)) {
key = instanceGetter.getKeyForType(object.getClass());
}
return key;
}
public static Object provideMetadataIfPossible(Object object) {
InstanceGetter instanceGetter = InstanceGetter.defaultInstanceGetter();
if (instanceGetter.isStorableAsMetadata(object)) {
@SuppressWarnings("unchecked")
Class<Object> objectType = (Class<Object>) object.getClass();
object = instanceGetter.providesMetadataFromGenericObject(objectType).provide(object);
}
return object;
}
public static String simpleName(Class<?> type) {
if (type == null) {
return null;
}
String simpleName = type.getSimpleName();
if (simpleName.isEmpty()) {
simpleName = type.getName().replaceAll(".*//.", "");
}
return simpleName;
}
/**
* Determine how to represent the specified type as metadata. For all the types the metadata
* system was designed to handle natively (int, Map, etc.) and for types handled by the
* {@link InstanceGetter} mechanism, the appropriate base class or interface used to set up
* serialization/deserialization is returned. If the type is not one of these directly supported
* types, null is returned.
* <p>
* The interface {@link java.io.Serializable} is a special case. If the specified type is not one
* of the directly-supported types described above, but the type is assignable to Serializable,
* this method returns null. This signifies that the type in question is not supported by the
* primary metadata mechanism. However, while this method returns null, the method
* {@link SettableMetadata#checkStorable(Object)} returns Serializable.class in this case. This
* works because there is a secondary mechanism for Serializable items, in which they are
* serialized into a String. This is for dealing with hierarchies for which no explicit metadata
* rules are set up for whatever reason.
*
* @param type the actual type of object to be represented as metadata
* @return the type the metadata system will use to store/retrieve such objects
*/
public static Class<?> classifyStorableType(Class<?> type) {
if (type == null) {
return null;
}
if (InstanceGetter.defaultInstanceGetter().isTypeStorableAsMetadata(type)) {
return ProvidesMetadataFromGenericObject.class;
}
if (Key.class.isAssignableFrom(type)) {
return Key.class;
}
if (Metadata.class.isAssignableFrom(type)) {
return Metadata.class;
}
if (StorableAsMetadata.class.isAssignableFrom(type)) {
return StorableAsMetadata.class;
}
if (Version.class.isAssignableFrom(type)) {
return Version.class;
}
if (List.class.isAssignableFrom(type)) {
return List.class;
}
if (SortedMap.class.isAssignableFrom(type)) {
return SortedMap.class;
}
if (Map.class.isAssignableFrom(type)) {
return Map.class;
}
if (SortedSet.class.isAssignableFrom(type)) {
return SortedSet.class;
}
if (Set.class.isAssignableFrom(type)) {
return Set.class;
}
if (String.class.isAssignableFrom(type)) {
return String.class;
}
if (Character.class.isAssignableFrom(type)) {
return Character.class;
}
if (Boolean.class.isAssignableFrom(type)) {
return Boolean.class;
}
if (Double.class.isAssignableFrom(type)) {
return Double.class;
}
if (Float.class.isAssignableFrom(type)) {
return Float.class;
}
if (Integer.class.isAssignableFrom(type)) {
return Integer.class;
}
if (Long.class.isAssignableFrom(type)) {
return Long.class;
}
if (Short.class.isAssignableFrom(type)) {
return Short.class;
}
if (Byte.class.isAssignableFrom(type)) {
return Byte.class;
}
if (Date.class.isAssignableFrom(type)) {
return Date.class;
}
if (String[].class.isAssignableFrom(type)) {
return String[].class;
}
if (Character[].class.isAssignableFrom(type)) {
return Character[].class;
}
if (Boolean[].class.isAssignableFrom(type)) {
return Boolean[].class;
}
if (Double[].class.isAssignableFrom(type)) {
return Double[].class;
}
if (Float[].class.isAssignableFrom(type)) {
return Float[].class;
}
if (Integer[].class.isAssignableFrom(type)) {
return Integer[].class;
}
if (Long[].class.isAssignableFrom(type)) {
return Long[].class;
}
if (Short[].class.isAssignableFrom(type)) {
return Short[].class;
}
if (Byte[].class.isAssignableFrom(type)) {
return Byte[].class;
}
if (Date[].class.isAssignableFrom(type)) {
return Date[].class;
}
if (Metadata[].class.isAssignableFrom(type)) {
return Metadata[].class;
}
if (char[].class.isAssignableFrom(type)) {
return char[].class;
}
if (boolean[].class.isAssignableFrom(type)) {
return boolean[].class;
}
if (double[].class.isAssignableFrom(type)) {
return double[].class;
}
if (float[].class.isAssignableFrom(type)) {
return float[].class;
}
if (int[].class.isAssignableFrom(type)) {
return int[].class;
}
if (long[].class.isAssignableFrom(type)) {
return long[].class;
}
if (short[].class.isAssignableFrom(type)) {
return short[].class;
}
if (byte[].class.isAssignableFrom(type)) {
return byte[].class;
}
if (Class.class.isAssignableFrom(type)) {
return Class.class;
}
return null;
}
}

View File

@@ -0,0 +1,77 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
public class ClassIO implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {
private static final InstanceGetter instanceGetter = InstanceGetter.defaultInstanceGetter();
@Override
public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
String typeId = null;
try {
Key<?> key = instanceGetter.getKeyForType(src);
typeId = key.getId();
} catch (@SuppressWarnings("unused") Exception e) {
}
if (typeId == null) {
DataTypeInfo info = DataTypeInfo.of(src);
if (info != DataTypeInfo.NULL) {
typeId = info.getTypeId();
}
}
if (typeId == null) {
typeId = src.getName();
}
return new JsonPrimitive(typeId);
}
@Override
public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
String typeId = json.getAsJsonPrimitive().getAsString();
Class<?> result = null;
try {
result = instanceGetter.getTypeForKey(Key.of(typeId));
} catch (@SuppressWarnings("unused") Exception e) {
}
if (result == null) {
try {
DataTypeInfo info = DataTypeInfo.of(typeId);
result = info.getTypeClass();
} catch (@SuppressWarnings("unused") Exception e) {
}
}
if (result == null) {
try {
result = Class.forName(typeId);
} catch (@SuppressWarnings("unused") Exception e) {
result = Object.class;
}
}
return result;
}
}

View File

@@ -0,0 +1,178 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import com.google.gson.reflect.TypeToken;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.StorableAsMetadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
/**
* Enumerations representing different forms of type information associated with particular classes.
* These enumerations are used via reflection to serialize objects, so it is important not to remove
* any of these enumerations, or the associated types of objects will not be serializable. This is
* true even if the particular enumerated items are not explicitly referenced.
*
* This enumeration must be kept consistent with the object types supported by the metadata package.
*/
enum DataTypeInfo {
////////////////////////////////////////////////////////////////
// Metadata-specific types.
METADATA_KEY("Key", Key.class, new TypeToken<Key<?>>() {}.getType()), //
METADATA("Metadata", Metadata.class, new TypeToken<Metadata>() {}.getType()), //
VERSION("Version", Version.class, new TypeToken<Version>() {}.getType()), //
ELEMENT("Element", GsonElement.class, new TypeToken<GsonElement>() {}.getType()), //
PROXIED_OBJECT("ProxiedObject", StorableAsMetadata.class,
new TypeToken<StorableAsMetadata<?>>() {}.getType()),
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Collection types.
LIST("List", List.class, new TypeToken<List<?>>() {}.getType()), //
SORTED_MAP("SortedMap", SortedMap.class, new TypeToken<SortedMap<?, ?>>() {}.getType()), //
MAP("Map", Map.class, new TypeToken<Map<?, ?>>() {}.getType()), //
SORTED_SET("SortedSet", SortedSet.class, new TypeToken<SortedSet<?>>() {}.getType()), //
SET("Set", Set.class, new TypeToken<Set<?>>() {}.getType()), //
ITERABLE("Iterable", Iterable.class, new TypeToken<Iterable<?>>() {}.getType()), //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Common object types.
STRING("String", String.class, new TypeToken<String>() {}.getType()), //
CHARACTER_OBJECT("Character", Character.class, new TypeToken<Character>() {}.getType()), //
BOOLEAN_OBJECT("Boolean", Boolean.class, new TypeToken<Boolean>() {}.getType()), //
// Floating-point types.
DOUBLE_OBJECT("Double", Double.class, new TypeToken<Double>() {}.getType()), //
FLOAT_OBJECT("Float", Float.class, new TypeToken<Float>() {}.getType()), //
// Integer types.
INTEGER_OBJECT("Integer", Integer.class, new TypeToken<Integer>() {}.getType()), //
LONG_OBJECT("Long", Long.class, new TypeToken<Long>() {}.getType()), //
SHORT_OBJECT("Short", Short.class, new TypeToken<Short>() {}.getType()), //
BYTE_OBJECT("Byte", Byte.class, new TypeToken<Byte>() {}.getType()), //
DATE_OBJECT("Date", Date.class, new TypeToken<Date>() {}.getType()), //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Arrays of common object types.
STRING_ARRAY("String Array", String[].class, new TypeToken<String[]>() {}.getType()), //
CHARACTER_OBJECT_ARRAY("Character Array", Character[].class,
new TypeToken<Character[]>() {}.getType()), //
BOOLEAN_OBJECT_ARRAY("Boolean Array", Boolean[].class, new TypeToken<Boolean[]>() {}.getType()), //
// Floating-point types.
DOUBLE_OBJECT_ARRAY("Double Array", Double[].class, new TypeToken<Double[]>() {}.getType()), //
FLOAT_OBJECT_ARRAY("Float Array", Float[].class, new TypeToken<Float[]>() {}.getType()), //
// Integer types.
INTEGER_OBJECT_ARRAY("Integer Array", Integer[].class, new TypeToken<Integer[]>() {}.getType()), //
LONG_OBJECT_ARRAY("Long Array", Long[].class, new TypeToken<Long[]>() {}.getType()), //
SHORT_OBJECT_ARRAY("Short Array", Short[].class, new TypeToken<Short[]>() {}.getType()), //
BYTE_OBJECT_ARRAY("Byte Array", Byte[].class, new TypeToken<Byte[]>() {}.getType()), //
DATE_ARRAY("Date Array", Date[].class, new TypeToken<Date[]>() {}.getType()), //
METADATA_ARRAY("metadata Array", Metadata[].class, new TypeToken<Metadata[]>() {}.getType()), //
////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////
// Arrays of primitive types.
CHARACTER_ARRAY("char Array", char[].class, new TypeToken<char[]>() {}.getType()), //
BOOLEAN_ARRAY("boolean Array", boolean[].class, new TypeToken<boolean[]>() {}.getType()), //
// Floating-point types.
DOUBLE_ARRAY("double Array", double[].class, new TypeToken<double[]>() {}.getType()), //
FLOAT_ARRAY("float Array", float[].class, new TypeToken<float[]>() {}.getType()), //
// Integer types.
INTEGER_ARRAY("int Array", int[].class, new TypeToken<int[]>() {}.getType()), //
LONG_ARRAY("long Array", long[].class, new TypeToken<long[]>() {}.getType()), //
SHORT_ARRAY("short Array", short[].class, new TypeToken<short[]>() {}.getType()), //
BYTE_ARRAY("byte Array", byte[].class, new TypeToken<byte[]>() {}.getType()), //
////////////////////////////////////////////////////////////////
// In a class by itself: Class:
CLASS("Class", Class.class, new TypeToken<Class<?>>() {}.getType()), //
SERIALIZABLE("Serializable", Serializable.class, new TypeToken<Serializable>() {}.getType()), //
// Catch-all case used both to handle nulls and to detect objects that cannot be serialized.
NULL("Null", Object.class, new TypeToken<Object>() {}.getType()), //
;
public static DataTypeInfo of(String typeId) {
for (DataTypeInfo info : values()) {
if (info.typeId.equals(typeId)) {
return info;
}
}
throw new IllegalArgumentException("Cannot decode unknown type " + typeId);
}
public static DataTypeInfo of(Class<?> valueClass) {
for (DataTypeInfo info : values()) {
if (info.valueClass.isAssignableFrom(valueClass)) {
return info;
}
}
// The final type info matches Object, which should be assignable from anything.
throw new AssertionError();
}
public static DataTypeInfo of(Type type) {
for (DataTypeInfo info : values()) {
if (info.type.equals(type)) {
return info;
}
}
throw new IllegalArgumentException();
}
public static DataTypeInfo forObject(Object object) {
DataTypeInfo result = NULL;
if (object != null) {
if (InstanceGetter.defaultInstanceGetter().isStorableAsMetadata(object)) {
result = METADATA;
}
if (result == NULL) {
result = of(object.getClass());
}
if (result == NULL) {
throw new IllegalArgumentException("Cannot serialize object of type "
+ Utilities.simpleName(object.getClass()) + " to JSON format");
}
}
return result;
}
private final String typeId;
private final Class<?> valueClass;
private final Type type;
private DataTypeInfo(String typeId, Class<?> valueClass, Type type) {
this.typeId = typeId;
this.valueClass = valueClass;
this.type = type;
}
public String getTypeId() {
return typeId;
}
public Class<?> getTypeClass() {
return valueClass;
}
public Type getType() {
return type;
}
@Override
public String toString() {
return "TypeInfo: " + typeId + ", Type = " + type + ", class = "
+ Utilities.simpleName(valueClass);
}
}

View File

@@ -0,0 +1,201 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.Map.Entry;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
final class GsonElement {
static final class ElementIO
implements JsonSerializer<GsonElement>, JsonDeserializer<GsonElement> {
@Override
public GsonElement deserialize(JsonElement jsonElement, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Preconditions.checkArgument(DataTypeInfo.ELEMENT.getType().equals(typeOfT));
return GsonElement.of(jsonElement, context);
}
@Override
public JsonElement serialize(GsonElement src, Type typeOfSrc,
JsonSerializationContext context) {
Preconditions.checkArgument(DataTypeInfo.ELEMENT.getType().equals(typeOfSrc));
return src.toElement(context);
}
}
private static final InstanceGetter INSTANCE_GETTER = InstanceGetter.defaultInstanceGetter();
static final String VALUE_TYPE_KEY = "valueType";
public static JsonElement encodeItem(Object item, boolean excludeMetadataVersion,
JsonSerializationContext context) {
item = Utilities.provideMetadataIfPossible(item);
DataTypeInfo typeInfo = DataTypeInfo.forObject(item);
if (excludeMetadataVersion && typeInfo == DataTypeInfo.METADATA) {
return MetadataIOv2.encodeWithoutVersion((Metadata) item, context);
}
return context.serialize(item, typeInfo.getType());
}
public static JsonElement encodeItemWithType(Object item, boolean excludeMetadataVersion,
JsonSerializationContext context) {
Key<? extends Object> typeKey = Utilities.provideTypeKeyIfPossible(item);
item = Utilities.provideMetadataIfPossible(item);
String typeId = typeKey != null ? typeKey.getId() : DataTypeInfo.forObject(item).getTypeId();
JsonObject result = new JsonObject();
result.add(typeId, encodeItem(item, excludeMetadataVersion, context));
return result;
}
public static void encodeTypeInfo(String typeId, JsonObject encodedItem) {
encodedItem.addProperty(VALUE_TYPE_KEY, typeId);
}
/**
* Decode item according to the supplied type, using the supplied version (if not null) for all
* Metadata.
*
* @param encodedItem
* @param typeInfo
* @param context
* @return
*/
public static Object decodeItem(JsonElement encodedItem, Key<?> typeKey, Version commonVersion,
JsonDeserializationContext context) {
if (INSTANCE_GETTER.isProvidableFromMetadata(typeKey)) {
Metadata metadata =
(Metadata) decodeItem(encodedItem, DataTypeInfo.METADATA, commonVersion, context);
return INSTANCE_GETTER.providesGenericObjectFromMetadata(typeKey).provide(metadata);
}
DataTypeInfo typeInfo = DataTypeInfo.of(typeKey.getId());
return decodeItem(encodedItem, typeInfo, commonVersion, context);
}
private static Object decodeItem(JsonElement encodedItem, DataTypeInfo typeInfo,
Version commonVersion, JsonDeserializationContext context) {
if (commonVersion != null && typeInfo == DataTypeInfo.METADATA) {
return MetadataIOv2.decode(encodedItem.getAsJsonObject(), commonVersion, context);
}
return context.deserialize(encodedItem, typeInfo.getType());
}
/**
* Decode item using self-contained type informtion but with the supplied Version for decoding
* compressed Metadata.
*
* @param encodedItem
* @param commonVersion
* @param context
* @return
*/
public static Object decodeItem(JsonElement encodedItem, Version commonVersion,
JsonDeserializationContext context) {
Preconditions.checkArgument(encodedItem.isJsonObject());
JsonObject encodedTypeAndValue = encodedItem.getAsJsonObject();
Preconditions.checkState(encodedTypeAndValue.size() == 1);
// Easiest way to get to the single entry is just to use a one-time loop.
// That way we don't have to mess with the entry set, iterators etc.
for (Entry<String, JsonElement> entry : encodedTypeAndValue.entrySet()) {
Key<?> proxyTypeKey = Key.of(entry.getKey());
JsonElement encodedValue = entry.getValue();
if (INSTANCE_GETTER.isProvidableFromMetadata(proxyTypeKey)) {
Metadata metadata =
(Metadata) decodeItem(encodedValue, DataTypeInfo.METADATA, commonVersion, context);
return INSTANCE_GETTER.providesGenericObjectFromMetadata(proxyTypeKey).provide(metadata);
}
DataTypeInfo typeInfo = DataTypeInfo.of(proxyTypeKey.getId());
return decodeItem(encodedValue, typeInfo, commonVersion, context);
}
// Can't get here because the loop above is guaranteed to execute at least once.
throw new AssertionError();
}
public static Key<?> decodeTypeInfo(JsonObject encodedItem) {
Key<?> typeKey = null;
if (encodedItem.has(VALUE_TYPE_KEY)) {
JsonElement encodedType = encodedItem.get(VALUE_TYPE_KEY);
typeKey = Key.of(encodedType.getAsString());
}
return typeKey;
}
public static GsonElement of(Object object) {
return new GsonElement(object, DataTypeInfo.forObject(object));
}
private static GsonElement of(JsonElement element, JsonDeserializationContext context) {
Preconditions.checkNotNull(element);
Preconditions.checkNotNull(context);
Preconditions.checkArgument(element.isJsonObject());
JsonObject valueObject = element.getAsJsonObject();
Set<Entry<String, JsonElement>> entryList = valueObject.entrySet();
Preconditions.checkState(entryList.size() == 1);
Entry<String, JsonElement> entry = entryList.iterator().next();
DataTypeInfo objectInfo = DataTypeInfo.of(entry.getKey());
Object object = context.deserialize(entry.getValue(), objectInfo.getType());
return new GsonElement(object, objectInfo);
}
private final Object object;
private final DataTypeInfo objectInfo;
private GsonElement(Object object, DataTypeInfo objectInfo) {
Preconditions.checkNotNull(objectInfo);
this.object = object;
this.objectInfo = objectInfo;
}
public JsonElement toElement(JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add(objectInfo.getTypeId(), context.serialize(object, objectInfo.getType()));
return result;
}
public Object getValue() {
return object;
}
}

View File

@@ -0,0 +1,788 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
import edu.jhuapl.ses.jsqrl.api.RepresentableAsMetadata;
import edu.jhuapl.ses.jsqrl.api.Serializer;
import edu.jhuapl.ses.jsqrl.api.StorableAsMetadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
import edu.jhuapl.ses.jsqrl.impl.EmptyMetadata;
import edu.jhuapl.ses.jsqrl.impl.MetadataManagerCollection;
import edu.jhuapl.ses.jsqrl.impl.gson.GsonElement.ElementIO;
public class GsonSerializer implements Serializer {
// Release notes are now in the package-info file.
// Encapsulation versions.
// This version supports serializing proxy class metadata using the same format used
// by standard Metadata (as in the Metadata interface). Instead of the key "Metadata",
// the name of the type serves as the key in the output file format. Previous versions used a
// separate proxy encapsulation. For backward compatibility, the proxy is still supported.
private static final Version SERIALIZER_VERSION_4 = Version.of(4, 0);
// This version reads and writes syntactically correct JSON format files. Previous versions
// were flawed in that they stored two consecutive JSON objects at the top level of the file.
private static final Version SERIALIZER_VERSION_3 = Version.of(3, 0);
// This version added support for:
// 1) saving/loading heterogeneous collections and
// 2) compressed format when a collection of Metadata objects happen to have the same
// version. In this case the version is stored once for the whole array or collection.
private static final Version SERIALIZER_VERSION_2 = Version.of(2, 0);
// Initial version.
private static final Version SERIALIZER_VERSION_1 = Version.of(1, 0);
private final MetadataManagerCollection managerCollection;
public static GsonSerializer of() {
return new GsonSerializer();
}
protected GsonSerializer() {
this.managerCollection = MetadataManagerCollection.of();
}
@Override
public Version getVersion() {
return SERIALIZER_VERSION_4;
}
@Override
public void register(Key<? extends Metadata> key, MetadataManager manager) {
managerCollection.add(key, manager);
}
@Override
public void deregister(Key<? extends Metadata> key) {
managerCollection.remove(key);
}
@Override
public void load(File file) throws IOException {
Preconditions.checkNotNull(file);
Version fileVersion = loadVersion(file);
if (SERIALIZER_VERSION_3.compareTo(fileVersion) > 0) {
loadBeforeV3(file);
return;
}
Gson gson = configureGson(fileVersion);
try (JsonReader reader = gson.newJsonReader(new FileReader(file))) {
Metadata source = gson.fromJson(reader, DataTypeInfo.METADATA.getType());
retrieveInSingleThreadContext(source);
}
}
@Override
public void save(File file) throws IOException {
Preconditions.checkNotNull(file);
File dir = file.getParentFile();
if (!dir.exists()) {
dir.mkdirs();
}
save(file, getVersion());
}
private Version loadVersion(File file) throws IOException {
Preconditions.checkNotNull(file);
Gson gson = createGsonBuilder().create();
try (JsonReader jsonReader = gson.newJsonReader(new FileReader(file))) {
jsonReader.beginObject();
String name = jsonReader.nextName();
if (!name.equals(DataTypeInfo.VERSION.getTypeId())) {
throw new IOException("Invalid Metadata file format");
}
String versionString = jsonReader.nextString();
return Version.of(versionString);
}
}
private void loadBeforeV3(File file) throws IOException {
Preconditions.checkNotNull(file);
SettableMetadata source = SettableMetadata.of(Version.of(0, 0));
Gson versionOnlyGson = configureGsonToReadVersionOnly();
try (JsonReader reader = versionOnlyGson.newJsonReader(new FileReader(file))) {
Version fileVersion = null;
try {
fileVersion = versionOnlyGson.fromJson(reader, DataTypeInfo.VERSION.getType());
} catch (Exception e) {
throw new IOException("Metadata reader version " + getVersion()
+ " cannot read metadata format of file " + file, e);
}
Gson gson = configureGson(fileVersion);
Map<String, Metadata> metadataMap = gson.fromJson(reader, DataTypeInfo.MAP.getType());
for (Entry<String, Metadata> entry : metadataMap.entrySet()) {
source.put(Key.of(entry.getKey()), entry.getValue());
}
}
retrieveInSingleThreadContext(source);
}
private void save(File file, Version version) throws IOException {
if (version.equals(SERIALIZER_VERSION_1)) {
saveV1(file);
return;
} else if (version.equals(SERIALIZER_VERSION_2)) {
saveV2(file);
return;
}
Gson gson = configureGson(version);
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = gson.newJsonWriter(fileWriter)) {
SettableMetadata metaMetadata = SettableMetadata.of(version);
Map<String, Metadata> metadataMap = new LinkedHashMap<>();
for (Key<? extends Metadata> key : managerCollection.getKeys()) {
MetadataManager manager = managerCollection.getManager(key);
Metadata metadata = manager.store();
if (!EmptyMetadata.instance().equals(metadata)) {
metaMetadata.put(Key.of(key.getId()), metadata);
metadataMap.put(key.getId(), metadata);
}
}
gson.toJson(metaMetadata, DataTypeInfo.METADATA.getType(), jsonWriter);
fileWriter.write('\n');
}
}
}
private void saveV2(File file) throws IOException {
Preconditions.checkNotNull(file);
Gson gson = configureGson(SERIALIZER_VERSION_2);
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = gson.newJsonWriter(fileWriter)) {
Map<String, Metadata> metadataMap = new HashMap<>();
for (Key<? extends Metadata> key : managerCollection.getKeys()) {
MetadataManager manager = managerCollection.getManager(key);
Metadata metadata = manager.store();
if (!EmptyMetadata.instance().equals(metadata)) {
metadataMap.put(key.getId(), metadata);
}
}
gson.toJson(SERIALIZER_VERSION_2, DataTypeInfo.VERSION.getType(), jsonWriter);
jsonWriter.flush();
fileWriter.write('\n');
gson.toJson(metadataMap, DataTypeInfo.MAP.getType(), jsonWriter);
jsonWriter.flush();
fileWriter.write('\n');
}
}
}
private void saveV1(File file) throws IOException {
Preconditions.checkNotNull(file);
Gson gson = configureGson(SERIALIZER_VERSION_1);
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = gson.newJsonWriter(fileWriter)) {
Map<String, Metadata> metadataMap = new HashMap<>();
for (Key<? extends Metadata> key : managerCollection.getKeys()) {
MetadataManager manager = managerCollection.getManager(key);
Metadata metadata = manager.store();
if (!EmptyMetadata.instance().equals(metadata)) {
metadataMap.put(key.getId(), metadata);
}
}
gson.toJson(SERIALIZER_VERSION_1, DataTypeInfo.VERSION.getType(), jsonWriter);
jsonWriter.flush();
fileWriter.write('\n');
gson.toJson(metadataMap, DataTypeInfo.MAP.getType(), jsonWriter);
jsonWriter.flush();
fileWriter.write('\n');
}
}
}
private void retrieveInSingleThreadContext(Metadata source) {
for (Key<? extends Metadata> key : managerCollection.getKeys()) {
if (source.hasKey(key)) {
Metadata element = source.get(key);
if (element != null) {
managerCollection.getManager(key).retrieve(element);
}
}
}
}
private static Gson configureGsonToReadVersionOnly() {
GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(DataTypeInfo.VERSION.getType(), new GsonVersionIO());
return builder.create();
}
private static Gson configureGson(Version serializerVersion) {
if (SERIALIZER_VERSION_1.equals(serializerVersion)) {
return configureGsonV1();
}
return configureGsonBuilder().create();
}
private static GsonBuilder createGsonBuilder() {
GsonBuilder builder = new GsonBuilder();
builder.serializeNulls();
builder.setPrettyPrinting();
builder.serializeSpecialFloatingPointValues();
builder.disableHtmlEscaping();
return builder;
}
private static GsonBuilder configureGsonBuilder() {
GsonBuilder builder = createGsonBuilder();
builder.registerTypeAdapter(DataTypeInfo.SORTED_SET.getType(), new SortedSetIOv2());
builder.registerTypeAdapter(DataTypeInfo.SET.getType(), new SetIOv2());
builder.registerTypeAdapter(DataTypeInfo.LIST.getType(), new ListIOv2());
builder.registerTypeAdapter(DataTypeInfo.ITERABLE.getType(), new ListIOv2());
builder.registerTypeAdapter(DataTypeInfo.SORTED_MAP.getType(), new SortedMapIOv2());
builder.registerTypeAdapter(DataTypeInfo.MAP.getType(), new MapIOv2());
builder.registerTypeAdapter(DataTypeInfo.METADATA.getType(), new MetadataIOv2());
builder.registerTypeAdapter(DataTypeInfo.VERSION.getType(), new GsonVersionIO());
builder.registerTypeAdapter(DataTypeInfo.ELEMENT.getType(), new ElementIO());
builder.registerTypeAdapter(DataTypeInfo.PROXIED_OBJECT.getType(), new ProxyIOv2<>());
builder.registerTypeAdapter(DataTypeInfo.CLASS.getType(), new ClassIO());
builder.registerTypeAdapter(DataTypeInfo.SERIALIZABLE.getType(), new SerializableIO());
return builder;
}
private static Gson configureGsonV1() {
GsonBuilder builder = createGsonBuilder();
builder.registerTypeAdapter(DataTypeInfo.SORTED_SET.getType(), new SortedSetIOv1());
builder.registerTypeAdapter(DataTypeInfo.SET.getType(), new SetIOv1());
builder.registerTypeAdapter(DataTypeInfo.LIST.getType(), new ListIOv1());
builder.registerTypeAdapter(DataTypeInfo.ITERABLE.getType(), new ListIOv1());
builder.registerTypeAdapter(DataTypeInfo.SORTED_MAP.getType(), new SortedMapIOv1());
builder.registerTypeAdapter(DataTypeInfo.MAP.getType(), new MapIOv1());
builder.registerTypeAdapter(DataTypeInfo.METADATA.getType(), new MetadataIOv1());
builder.registerTypeAdapter(DataTypeInfo.VERSION.getType(), new GsonVersionIO());
builder.registerTypeAdapter(DataTypeInfo.ELEMENT.getType(), new ElementIO());
builder.registerTypeAdapter(DataTypeInfo.PROXIED_OBJECT.getType(), new ProxyIOv1<>());
builder.registerTypeAdapter(DataTypeInfo.CLASS.getType(), new ClassIO());
return builder.create();
}
/**
* This enumeration demonstrates how to serialize/deserialize an enumeration by implementing the
* StorableAsMetadata interface.
*
*/
private static final Key<String> NAME = Key.of("Name");
private static final Key<TestEnum> TEST_ENUM_PROXY_KEY = Key.of("TestEnum");
private static final Version VERSION = Version.of(1, 0);
private enum TestEnum implements StorableAsMetadata<TestEnum> {
OPTION0("Option 0"), OPTION1("Option 1");
// Need to live with this warning until the register method has been phased out.
public static void register(InstanceGetter instanceGetter) {
instanceGetter.register(TEST_ENUM_PROXY_KEY, (metadata) -> {
return valueOf(metadata.get(NAME));
});
}
private final String text;
TestEnum(String text) {
this.text = text;
}
@Override
public String toString() {
return text;
}
@Override
public Key<TestEnum> getKey() {
return TEST_ENUM_PROXY_KEY;
}
@Override
public Metadata store() {
return SettableMetadata.of(VERSION).put(NAME, name());
}
}
/**
* This enumeration demonstrates how to serialize/deserialize an enumeration without implementing
* StorableAsMetadata.
*/
private enum TestEnum2 {
OPTION0, OPTION1;
}
private static final Key<TestEnum2> TEST_ENUM2_PROXY_KEY = Key.of("TestEnum2");
private static void registerTestEnum2(InstanceGetter instanceGetter) {
instanceGetter.register(TEST_ENUM2_PROXY_KEY, metadata -> {
return TestEnum2.valueOf(metadata.get(NAME));
}, TestEnum2.class, object -> {
return SettableMetadata.of(VERSION).put(NAME, object.name());
});
}
private static class TestManager implements MetadataManager {
private final SettableMetadata metadata;
TestManager(SettableMetadata metadata) {
this.metadata = metadata;
}
@Override
public Metadata store() {
SettableMetadata destination = SettableMetadata.of(metadata.getVersion());
for (Key<?> key : metadata.getKeys()) {
@SuppressWarnings("unchecked")
Key<Object> newKey = (Key<Object>) key;
destination.put(newKey, metadata.get(key));
}
return destination;
}
@Override
public void retrieve(Metadata source) {
metadata.clear();
for (Key<?> key : source.getKeys()) {
Object value = source.get(key);
@SuppressWarnings("unchecked")
Key<Object> newKey = (Key<Object>) key;
metadata.put(newKey, value);
}
}
}
// These are all just fodder for test cases, These version numbers have nothing to do with the
// version of
// the serializer.
private static final Version SAMPLE_METADATA_VERSION = Version.of(1, 0);
private static final Version SAMPLE_SUB_METADATA_VERSION = Version.of(3, 1);
private static final String SUB_METADATA_ID = "Bennu / V3";
private static final Key<SettableMetadata> SAMPLE_METADATA_KEY = Key.of("testState");
private static final Key<SettableMetadata> SAMPLE_SUB_METADATA_KEY = Key.of(SUB_METADATA_ID);
private static final Key<List<List<String>>> LIST_LIST_STRING_KEY = Key.of("listListString");
public static void main(String[] args) throws IOException {
TestEnum.register(InstanceGetter.defaultInstanceGetter());
registerTestEnum2(InstanceGetter.defaultInstanceGetter());
// Test failure when trying to serialize something that can't be serialized.
{
class UnserializableObject {
};
UnserializableObject unserializableObject = new UnserializableObject();
try {
SettableMetadata.of(SAMPLE_METADATA_VERSION).put(Key.of("unserializable"),
unserializableObject);
System.err.println("Saving an unserializable object did not throw an exception");
} catch (@SuppressWarnings("unused") IllegalArgumentException e) {
// This is as it should be.
}
}
// Test V1.
String testPath = Paths.get(System.getProperty("user.home"), "Downloads").toString();
{
SettableMetadata state = createV1SampleMetadata();
File file = Paths.get(testPath, "MyStateV1.sbmt").toFile();
testSaveAndReloadState(state, file, SERIALIZER_VERSION_1);
}
// Test V2.
{
SettableMetadata state = createV2SampleMetadata();
File file = Paths.get(testPath, "MyStateV2.sbmt").toFile();
testSaveAndReloadState(state, file, SERIALIZER_VERSION_2);
}
// Test V3.
{
SettableMetadata state = createV3SampleMetadata();
File file = Paths.get(testPath, "MyStateV3.sbmt").toFile();
testSaveAndReloadState(state, file, SERIALIZER_VERSION_3);
}
// Test current version.
{
SettableMetadata state = createV4SampleMetadata();
File file = Paths.get(testPath, "MyState.sbmt").toFile();
state = testSaveAndReloadState(state, file, SERIALIZER_VERSION_4);
SettableMetadata subState = state.get(SAMPLE_SUB_METADATA_KEY);
// Test some further unpacking.
state.get(Key.of("double array"));
state.get(Key.of("int array"));
state.get(Key.of("Double array"));
state.get(Key.of("Integer array"));
state.get(Key.of("String array"));
subState.get(Key.of("longNull"));
if (TestEnum.OPTION1 != subState.get(Key.of("testEnum"))) {
System.err.println("testEnum value was not option 1");
}
state.get(Key.of("stringSet"));
if (TestEnum2.OPTION1 != subState.get(Key.of("testEnum2"))) {
System.err
.println("testEnum2 value was " + subState.get(Key.of("testEnum2")) + ", not option 1");
}
// This fails at runtime. If this were a real key templated on Long it would fail at compile
// time.
// Float fVal = subState.get(Key.of("long"));
// This doesn't fail at runtime or compile time but I wish it would:
// List<List<Integer>> unpackedListList = state.get(Key.of("listListString"));
// But the following does fail to compile, which is probably good enough.
// unpackedListList = state.get(listListStringKey);
// And this fails at runtime, as it should.
// System.out.println(unpackedListList.get(1).get(1) * 7);
// It would be OK if this were to work but it doesn't.
// float fVal = subState.get(Key.of("resolution"));
// This one is supposed to throw an exception.
// Short longAsShort = subState.get(Key.of("facets"));
// System.err.println("long retrieved as a short is " + longAsShort);
}
}
private static SettableMetadata createV1SampleMetadata() {
SettableMetadata subState = SettableMetadata.of(SAMPLE_SUB_METADATA_VERSION);
TestEnum testEnumBefore = TestEnum.OPTION1;
subState.put(Key.of("tab"), "1");
subState.put(Key.of("facets"), 2000000001L);
subState.put(Key.of("showBaseMap"), true);
subState.put(Key.of("resolution"), -5.e64);
subState.put(Key.of("int"), 20);
subState.put(Key.of("long"), (long) 20);
subState.put(Key.of("short"), (short) 20);
subState.put(Key.of("byte"), (byte) 20);
subState.put(Key.of("double"), (double) 20);
subState.put(Key.of("float"), (float) 20);
subState.put(Key.of("char"), (char) 20);
subState.put(Key.of("boolean"), false);
subState.put(Key.of("string"), "a string");
subState.put(Key.of("htmlString"), "<pre>A literal escaped in html</pre>");
subState.put(Key.of("stringNull"), null);
subState.put(Key.of("longNull"), null);
subState.put(Key.of("testEnum"), testEnumBefore);
subState.put(Key.of("List<TestEnum>"), ImmutableList.of(TestEnum.OPTION1, TestEnum.OPTION0));
List<String> stringList = new ArrayList<>();
stringList.add("String0");
stringList.add(null);
stringList.add("String2");
Key<List<String>> stringListKey = Key.of("stringList");
subState.put(stringListKey, stringList);
List<Integer> intList = new ArrayList<>();
intList.add(0);
intList.add(null);
intList.add(2);
Key<List<Integer>> intListKey = Key.of("intList");
subState.put(intListKey, intList);
List<List<String>> listListString = new ArrayList<>();
listListString.add(null);
listListString.add(ImmutableList.of("X", "y", "z"));
listListString.add(stringList);
final SettableMetadata state = SettableMetadata.of(SAMPLE_METADATA_VERSION);
state.put(Key.of("Current View"), SUB_METADATA_ID);
state.put(Key.of("Tab Number"), new Integer(3));
state.put(Key.of("Current View2"), SUB_METADATA_ID);
state.put(LIST_LIST_STRING_KEY, listListString);
state.put(Key.of("stringSet"), ImmutableSortedSet.of("liver", "spleen", "aardvark"));
state.put(Key.of("Metadata"), "a string that happens to have the key \"Metadata\"");
// The following type has built-in support through DataTypeInfo.
state.put(Key.of("Double.class"), Double.class);
// The following type is registered with InstanceGetter. This should work using the registerd
// name.
state.put(Key.of("TestEnum2.class"), TestEnum2.class);
// The following type is not registered with InstanceGetter. This should also work using the
// full class name.
state.put(Key.of("Metadata.class"), RepresentableAsMetadata.class);
Map<Byte, Short> byteShortMap = new HashMap<>();
byteShortMap.put((byte) 1, null);
byteShortMap.put(null, (short) 12);
byteShortMap.put((byte) 11, (short) 23);
byteShortMap.put((byte) 10, (short) 17);
Key<Map<Byte, Short>> byteShortMapKey = Key.of("byteShortMap");
state.put(byteShortMapKey, byteShortMap);
SortedMap<Byte, Short> byteSortedMap = new TreeMap<>();
byteSortedMap.put((byte) 1, null);
byteSortedMap.put((byte) 11, (short) 23);
byteSortedMap.put((byte) 10, (short) 17);
Key<SortedMap<Byte, Short>> byteSortedMapKey = Key.of("byteSortedMap");
state.put(byteSortedMapKey, byteSortedMap);
SortedMap<String, SortedMap<Integer, String>> homogeneousMapMap = new TreeMap<>();
homogeneousMapMap.put("nullMap", null);
SortedMap<Integer, String> intMap = new TreeMap<>();
intMap.put(0, "zero");
intMap.put(1, "one");
homogeneousMapMap.put("map0", intMap);
homogeneousMapMap.put("map1", intMap);
state.put(Key.of("homogeneous map of map"), homogeneousMapMap);
homogeneousMapMap = state.get(Key.of("homogeneous map of map"));
SortedSet<String> treeSet = new TreeSet<>();
treeSet.add("string0");
state.put(Key.of("treeSet"), treeSet);
treeSet = state.get(Key.of("treeSet"));
state.put(Key.of("single element map"), ImmutableMap.of("key0", "value0"));
state.put(Key.of("single element list"), ImmutableList.of(7));
state.put(Key.of("double array"), new double[] {3., 4., 5.});
state.put(Key.of("int array"), new int[] {6, 7, 8});
state.put(Key.of("Double array"), new Double[] {9., 10., 11.});
state.put(Key.of("Integer array"), new Integer[] {12, 13, 14});
state.put(Key.of("String array"), new String[] {"a", "b", "c"});
state.put(SAMPLE_SUB_METADATA_KEY, subState);
return state;
}
private static SettableMetadata createV2SampleMetadata() {
// Start with the V1 metadata set.
SettableMetadata state = createV1SampleMetadata();
SettableMetadata subState = state.get(SAMPLE_SUB_METADATA_KEY);
// Add tests for things supported in V2 and above.
SortedMap<String, SortedMap<Integer, Object>> mapHeterogeneousMap = new TreeMap<>();
mapHeterogeneousMap.put("nullMap", null);
SortedMap<Integer, Object> nestedMap = new TreeMap<>();
nestedMap.put(0, "zero");
nestedMap.put(1, 1.);
mapHeterogeneousMap.put("map0", nestedMap);
mapHeterogeneousMap.put("map1", nestedMap);
state.put(Key.of("map of heterogenous maps"), mapHeterogeneousMap);
List<Integer> intList = subState.get(Key.of("intList"));
SortedMap<String, Object> mapOfCollections = new TreeMap<>();
mapOfCollections.put("nullMap", null);
mapOfCollections.put("map", nestedMap);
mapOfCollections.put("intList", intList);
state.put(Key.of("map containing nested objects"), mapOfCollections);
List<Object> objectList = new ArrayList<Object>();
objectList.add(null);
objectList.add("a");
objectList.add(1);
objectList.add(2.);
objectList.add(false);
objectList.add(SettableMetadata.of(Version.of(1, 3)));
objectList.add(SettableMetadata.of(Version.of(1, 3)).put(Key.of("key0"), "value0"));
subState.put(Key.of("objectList"), objectList);
Set<Object> objectSet = new HashSet<>();
objectSet.add("a");
objectSet.add(null);
objectSet.add(1);
objectSet.add(2.);
objectSet.add(true);
objectSet.add(SettableMetadata.of(Version.of(1, 3)).put(Key.of("key0"), "value0"));
subState.put(Key.of("objectSet"), objectSet);
Map<Object, Object> objectMap = new HashMap<>();
objectMap.put("key0", "repeatedValue");
objectMap.put("key1", "repeatedValue");
objectMap.put(null, "valueForNullKey");
objectMap.put("key2", "distinctValue");
subState.put(Key.of("homogeneousObjectMap"), objectMap);
try {
// Now test exception for heterogenous keys.
objectMap.put(7, "stringValue");
subState.put(Key.of("invalidObjectMap"), objectMap);
} catch (@SuppressWarnings("unused") IllegalArgumentException e) {
// This is as it should be.
}
objectMap = new HashMap<>();
objectMap.put("key0", "repeatedValue");
objectMap.put("key1", "repeatedValue");
objectMap.put(null, 0);
objectMap.put("key2", 4.2);
objectMap.put("meta0", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key0"), "value0"));
objectMap.put("meta1", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key1"), "value1"));
objectMap.put("meta2", SettableMetadata.of(Version.of(2, 3)).put(Key.of("key1"), "value1"));
subState.put(Key.of("heterogeneousObjectMap"), objectMap);
objectMap = new HashMap<>();
objectMap.put("key0", "repeatedValue");
objectMap.put("key1", "repeatedValue");
objectMap.put(null, 0);
objectMap.put("key2", 4.2);
objectMap.put("meta0", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key0"), "value0"));
objectMap.put("meta1", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key0"), "value0")
.put(Key.of("key1"), "value1"));
subState.put(Key.of("heterogeneousObjectHomogeneousMetadataMap"), objectMap);
objectMap = new HashMap<>();
objectMap.put("meta0", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key0"), "value0"));
objectMap.put("meta1", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key1"), "value1"));
subState.put(Key.of("homogeneousMetadataMap"), objectMap);
objectMap = new HashMap<>();
objectMap.put("meta0", SettableMetadata.of(Version.of(2, 2)).put(Key.of("key0"), "value0"));
objectMap.put("meta1", SettableMetadata.of(Version.of(2, 3)).put(Key.of("key1"), "value1"));
subState.put(Key.of("heterogeneousMetadataMap"), objectMap);
List<Metadata> metadataList = new ArrayList<>();
metadataList.add(SettableMetadata.of(Version.of(0, 1)).put(Key.of("key0"), "version 0.1"));
metadataList.add(SettableMetadata.of(Version.of(0, 1)).put(Key.of("key0"), "version 0.1")
.put(Key.of("key1"), "version 0.1"));
subState.put(Key.of("metadataList"), metadataList);
metadataList.add(SettableMetadata.of(Version.of(0, 2)).put(Key.of("key2"), "version 0.2"));
subState.put(Key.of("heterogeneousMetadataList"), metadataList);
Set<Metadata> metadataSet = new HashSet<>();
metadataSet.add(SettableMetadata.of(Version.of(0, 1)).put(Key.of("key0"), "version 0.1"));
metadataSet.add(SettableMetadata.of(Version.of(0, 1)).put(Key.of("key0"), "version 0.1")
.put(Key.of("key1"), "version 0.1"));
subState.put(Key.of("metadataSet"), metadataSet);
metadataSet.add(SettableMetadata.of(Version.of(0, 2)).put(Key.of("key2"), "version 0.2"));
subState.put(Key.of("heterogeneousMetadataSet"), metadataSet);
SettableMetadata homogeneousMetaMetadata = SettableMetadata.of(Version.of(2, 0));
homogeneousMetaMetadata.put(Key.of("md0"),
SettableMetadata.of(Version.of(2, 0)).put(Key.of("key0"), "value0"));
homogeneousMetaMetadata.put(Key.of("md1"),
SettableMetadata.of(Version.of(2, 0)).put(Key.of("key0"), 100).put(Key.of("key1"), 101.));
// subState.put(Key.of("homogeneousMetaMetadata"), homogeneousMetaMetadata);
return state;
}
private static SettableMetadata createV3SampleMetadata() {
// V3 was a change to the low level format; no difference in the types of objects that could be
// supported.
return createV2SampleMetadata();
}
private static SettableMetadata createV4SampleMetadata() {
SettableMetadata state = createV3SampleMetadata();
SettableMetadata subState = state.get(SAMPLE_SUB_METADATA_KEY);
// Starting in V4, possible to serialize proxy objects directly as metadata using newer
// capabilities of the InstanceGetter class.
TestEnum2 testEnum2Before = TestEnum2.OPTION1;
subState.put(Key.of("testEnum2"), testEnum2Before);
subState.put(Key.of("testEnum2List"), ImmutableList.of(TestEnum2.OPTION1, TestEnum2.OPTION0));
subState.put(Key.of("testEnum2Map"),
ImmutableMap.of("enumA", TestEnum2.OPTION1, "enumB", TestEnum2.OPTION0));
return state;
}
private static SettableMetadata testSaveAndReloadState(SettableMetadata originalState, File file,
Version saveVersion) throws IOException {
SettableMetadata originalSubState = originalState.get(SAMPLE_SUB_METADATA_KEY);
{
TestManager stateManager = new TestManager(originalState);
TestManager subStateManager = new TestManager(originalSubState);
GsonSerializer serializer = GsonSerializer.of();
serializer.register(SAMPLE_METADATA_KEY, stateManager);
serializer.register(SAMPLE_SUB_METADATA_KEY, subStateManager);
serializer.save(file, saveVersion);
}
{
// Blank states with the same keys as the originals.
SettableMetadata reloadedState = SettableMetadata.of(SAMPLE_METADATA_VERSION);
SettableMetadata reloadedSubState = SettableMetadata.of(SAMPLE_SUB_METADATA_VERSION);
TestManager stateManager = new TestManager(reloadedState);
TestManager subStateManager = new TestManager(reloadedSubState);
GsonSerializer serializer = GsonSerializer.of();
// Go in reverse order here just to test that the order doesn't need to be the same.
serializer.register(SAMPLE_SUB_METADATA_KEY, subStateManager);
serializer.register(SAMPLE_METADATA_KEY, stateManager);
serializer.load(file);
System.out.println("Reloaded " + saveVersion + " state was"
+ (originalState.equals(reloadedState) ? " " : " ******* NOT ******* ")
+ "found equal to original");
System.out.println("Reloaded " + saveVersion + " sampleState was"
+ (originalSubState.equals(reloadedSubState) ? " " : " ******* NOT ******* ")
+ "found equal to original");
return reloadedState;
}
}
}

View File

@@ -0,0 +1,65 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import edu.jhuapl.ses.jsqrl.api.Version;
final class GsonVersionIO implements JsonSerializer<Version>, JsonDeserializer<Version> {
private static final String KEY_ID = DataTypeInfo.VERSION.getTypeId();
public static JsonPrimitive encode(Version version) {
Preconditions.checkNotNull(version);
return new JsonPrimitive(version.toString());
}
public static Version decode(JsonPrimitive jsonVersion) {
Preconditions.checkNotNull(jsonVersion);
return Version.of(jsonVersion.getAsString());
}
public static Version decode(JsonObject jsonObject) {
Preconditions.checkNotNull(jsonObject);
JsonElement jsonVersion = jsonObject.get(KEY_ID);
Preconditions.checkArgument(jsonVersion.isJsonPrimitive());
return decode(jsonVersion.getAsJsonPrimitive());
}
@Override
public JsonElement serialize(Version src, @SuppressWarnings("unused") Type typeOfSrc,
@SuppressWarnings("unused") JsonSerializationContext context) {
JsonObject result = new JsonObject();
result.add(KEY_ID, encode(src));
return result;
}
@Override
public Version deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
@SuppressWarnings("unused") JsonDeserializationContext context) {
Preconditions.checkNotNull(jsonElement);
Preconditions.checkArgument(jsonElement.isJsonObject());
JsonObject object = jsonElement.getAsJsonObject();
// Unpack metadata.
JsonElement keyIdElement = object.get(KEY_ID);
if (keyIdElement == null || !keyIdElement.isJsonPrimitive()) {
throw new IllegalArgumentException(
"Field \"" + KEY_ID + "\" is missing or has wrong type in Json object");
}
return decode(keyIdElement.getAsJsonPrimitive());
}
}

View File

@@ -0,0 +1,144 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
abstract class IterableIOv1 implements JsonSerializer<Iterable<?>> {
/**
* Internal utility class; just here so the unpack method can return all the distinct pieces of
* information contained in the input JsonElement.
*/
protected static class DeserializedJsonArray {
protected final DataTypeInfo dataTypeInfo;
protected final JsonArray jsonArray;
protected DeserializedJsonArray(DataTypeInfo dataTypeInfo, JsonArray jsonArray) {
this.dataTypeInfo = dataTypeInfo;
this.jsonArray = jsonArray;
}
}
protected IterableIOv1() {
}
private static final String ITERABLE_VALUE_TYPE = "valueType";
private static final String ITERABLE_VALUE = "value";
@Override
public JsonElement serialize(Iterable<?> src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
JsonObject result = new JsonObject();
// First pass: if any entries are found, determine types of key and value based on the type of
// the first non-null element.
DataTypeInfo valueInfo = DataTypeInfo.NULL;
for (Object value : src) {
if (valueInfo == DataTypeInfo.NULL) {
valueInfo = DataTypeInfo.forObject(value);
}
if (valueInfo != DataTypeInfo.NULL) {
break;
}
}
// Second pass: write the collection's entries to a JsonArray.
JsonArray destArray = new JsonArray();
Type valueType = valueInfo.getType();
for (Object value : src) {
destArray.add(context.serialize(value, valueType));
}
// Put iterable metadata and data into the resultant object.
result.add(ITERABLE_VALUE_TYPE, context.serialize(valueInfo.getTypeId()));
result.add(ITERABLE_VALUE, destArray);
return result;
}
protected DeserializedJsonArray unpack(JsonElement jsonElement) {
if (!jsonElement.isJsonObject()) {
throw new IllegalArgumentException();
}
JsonObject object = jsonElement.getAsJsonObject();
// Unpack metadata.
JsonElement dataTypeElement = object.get(ITERABLE_VALUE_TYPE);
if (dataTypeElement == null || !dataTypeElement.isJsonPrimitive()) {
throw new IllegalArgumentException(
"Field \"" + ITERABLE_VALUE_TYPE + "\" is missing or has wrong type in Json object");
}
DataTypeInfo dataTypeInfo = DataTypeInfo.of(dataTypeElement.getAsString());
// Unpack data.
JsonElement dataElement = object.get(ITERABLE_VALUE);
if (dataElement == null || !dataElement.isJsonArray()) {
throw new IllegalArgumentException(
"Field \"" + ITERABLE_VALUE + "\" is missing or has wrong type in Json object");
}
return new DeserializedJsonArray(dataTypeInfo, dataElement.getAsJsonArray());
}
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
set1.add("One");
set1.add(null);
Set<String> set2 = new HashSet<>();
set2.add("Zero");
set2.add("Two");
List<Set<String>> createdList = new ArrayList<>();
createdList.add(set1);
createdList.add(set2);
Gson GSON = new GsonBuilder().serializeNulls()
.registerTypeAdapter(DataTypeInfo.of(SortedSet.class).getType(), new SortedSetIOv1())
.registerTypeAdapter(DataTypeInfo.of(Set.class).getType(), new SetIOv1())
.registerTypeAdapter(DataTypeInfo.of(List.class).getType(), new ListIOv1())
.setPrettyPrinting().create();
String testPath = Paths.get(System.getProperty("user.home"), "Downloads").toString();
String file = Paths.get(testPath, "test-iterables.json").toString();
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = GSON.newJsonWriter(fileWriter)) {
GSON.toJson(createdList, DataTypeInfo.forObject(createdList).getType(), jsonWriter);
fileWriter.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
try (JsonReader jsonReader = GSON.newJsonReader(new FileReader(file))) {
Iterable<?> readList = GSON.fromJson(jsonReader, DataTypeInfo.of(List.class).getType());
if (!readList.equals(createdList)) {
System.err.println("OUTPUT IS NOT EQUAL TO INPUT!!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,217 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
abstract class IterableIOv2 implements JsonSerializer<Iterable<?>> {
/**
* Iterate over objects that may include implementations of Metadata and extract the common
* Metadata Version, if there is one.
*
* @param iterable the objects to examine
* @return the version that all the metadata share, or null if either there are no Metadata
* objects, if the Metadata objects do not share a common version, or if there is only one
* Metadata object
*/
public static Version getCommonVersion(Iterable<?> iterable) {
Version result = null;
int metadataCount = 0;
for (Object item : iterable) {
if (item instanceof Metadata) {
++metadataCount;
Version itemVersion = ((Metadata) item).getVersion();
if (result == null) {
// First Metadata found -- provisionally make its Version the result.
result = itemVersion;
} else if (!result.equals(itemVersion)) {
// More than one Metadata was found, and it has a different Version from the provisional
// result. Discard the provisional result and don't look any further.
result = null;
break;
}
}
}
// If only one metadata object is present, do not return a "common" version.
if (metadataCount == 1) {
result = null;
}
return result;
}
protected IterableIOv2() {
}
private static final String VALUE_KEY = "value";
private static final Key<?> PRIVATE_NULL_KEY = Key.of("PRIVATE NULL KEY");
@Override
public JsonElement serialize(Iterable<?> src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
// First pass: if any entries are found, determine types of key and value based on the type of
// the first non-null element.
// Note that although iterableTypeInfo and iterableTypeKey are very similar, there are nuances
// that lead to the need to treat them slightly differently with respect to how null values are
// handled/categorized.
DataTypeInfo iterableTypeInfo = DataTypeInfo.NULL; // Meaning an entry is a null pointer.
Key<?> iterableTypeKey = null; // Meaning the instance getter could not decode this.
boolean sameType = true;
for (Object item : src) {
DataTypeInfo valueInfo = DataTypeInfo.forObject(item);
if (iterableTypeInfo == DataTypeInfo.NULL) {
iterableTypeInfo = valueInfo;
} else if (valueInfo != DataTypeInfo.NULL && valueInfo != iterableTypeInfo) {
sameType = false;
break;
}
// See if there is a provider for the type indicated by this key.
Key<?> valueTypeKey = Utilities.provideTypeKeyIfPossible(item);
if (valueTypeKey == null) {
// Use a null object rather than null pointer to distinguish between the cases "no provider"
// and "no provider YET".
valueTypeKey = PRIVATE_NULL_KEY;
}
if (iterableTypeKey == null) {
iterableTypeKey = valueTypeKey;
} else if (iterableTypeKey == PRIVATE_NULL_KEY || valueTypeKey == PRIVATE_NULL_KEY) {
// Use object equality for comparisons involving the private null key. This is to prevent a
// name collision should the user happen to define type key with the same name as the
// private null key.
if (iterableTypeKey != valueTypeKey) {
sameType = false;
break;
}
} else if (!iterableTypeKey.equals(valueTypeKey)) {
sameType = false;
break;
}
}
// The private null key is just for use within this method, so replace it with null before
// continuing.
if (iterableTypeKey == PRIVATE_NULL_KEY) {
iterableTypeKey = null;
}
// Extract metadata version, if any.
Version commonVersion = getCommonVersion(src);
boolean excludeMetadataVersionInValues = commonVersion != null;
// Second pass: write items, either with or without their types depending on whether they are
// the same.
JsonArray jsonArray = new JsonArray();
for (Object item : src) {
JsonElement encodedItem =
sameType ? GsonElement.encodeItem(item, excludeMetadataVersionInValues, context)
: GsonElement.encodeItemWithType(item, excludeMetadataVersionInValues, context);
jsonArray.add(encodedItem);
}
// Put iterable metadata and data into the resultant object.
JsonObject result = new JsonObject();
if (sameType) {
String iterableTypeId =
iterableTypeKey != null ? iterableTypeKey.getId() : iterableTypeInfo.getTypeId();
GsonElement.encodeTypeInfo(iterableTypeId, result);
}
if (excludeMetadataVersionInValues) {
MetadataIOv2.encodeVersion(commonVersion, result);
}
result.add(VALUE_KEY, jsonArray);
return result;
}
protected List<?> deserialize(JsonObject encodedIterable, JsonDeserializationContext context) {
Key<?> typeKey = GsonElement.decodeTypeInfo(encodedIterable);
Version commonVersion = MetadataIOv2.decodeVersion(encodedIterable);
JsonArray encodedArray = encodedIterable.get(VALUE_KEY).getAsJsonArray();
List<Object> result = new ArrayList<>();
for (JsonElement encodedItem : encodedArray) {
if (typeKey != null) {
result.add(GsonElement.decodeItem(encodedItem, typeKey, commonVersion, context));
} else {
result.add(GsonElement.decodeItem(encodedItem, commonVersion, context));
}
}
return result;
}
public static void main(String[] args) {
Set<String> set1 = new HashSet<>();
set1.add("One");
set1.add(null);
Set<String> set2 = new HashSet<>();
set2.add("Zero");
set2.add("Two");
List<Set<String>> createdList = new ArrayList<>();
createdList.add(set1);
createdList.add(set2);
Gson GSON = new GsonBuilder().serializeNulls()
.registerTypeAdapter(DataTypeInfo.of(SortedSet.class).getType(), new SortedSetIOv2())
.registerTypeAdapter(DataTypeInfo.of(Set.class).getType(), new SetIOv2())
.registerTypeAdapter(DataTypeInfo.of(List.class).getType(), new ListIOv2())
.setPrettyPrinting().create();
String testPath = Paths.get(System.getProperty("user.home"), "Downloads").toString();
String file = Paths.get(testPath, "test-iterables.json").toString();
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = GSON.newJsonWriter(fileWriter)) {
GSON.toJson(createdList, DataTypeInfo.forObject(createdList).getType(), jsonWriter);
fileWriter.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
try (JsonReader jsonReader = GSON.newJsonReader(new FileReader(file))) {
Iterable<?> readList = GSON.fromJson(jsonReader, DataTypeInfo.of(List.class).getType());
if (!readList.equals(createdList)) {
System.err.println("OUTPUT IS NOT EQUAL TO INPUT!!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,9 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.util.List;
import com.google.gson.JsonDeserializer;
interface ListIO extends JsonDeserializer<List<?>> {
}

View File

@@ -0,0 +1,35 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
final class ListIOv1 extends IterableIOv1 implements ListIO {
ListIOv1() {
}
@Override
public List<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
DeserializedJsonArray array = unpack(jsonElement);
DataTypeInfo dataInfo = array.dataTypeInfo;
JsonArray jsonArray = array.jsonArray;
// Create output data object.
List<?> result = new ArrayList<>();
Type valueType = dataInfo.getType();
for (JsonElement entryElement : jsonArray) {
result.add(context.deserialize(entryElement, valueType));
}
return result;
}
}

View File

@@ -0,0 +1,24 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.List;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
final class ListIOv2 extends IterableIOv2 implements ListIO {
ListIOv2() {
}
@Override
public List<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) {
Preconditions.checkArgument(jsonElement.isJsonObject());
return deserialize(jsonElement.getAsJsonObject(), context);
}
}

View File

@@ -0,0 +1,162 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
abstract class MapBaseIOv1 implements JsonSerializer<Map<?, ?>> {
/**
* Internal utility class; just here so the unpack method can return all the distinct pieces of
* information contained in the input JsonElement.
*/
protected static class DeserializedJsonObject {
protected final DataTypeInfo keyTypeInfo;
protected final DataTypeInfo valueTypeInfo;
protected final JsonObject jsonMap;
protected DeserializedJsonObject(DataTypeInfo keyTypeInfo, DataTypeInfo valueTypeInfo,
JsonObject jsonMap) {
this.keyTypeInfo = keyTypeInfo;
this.valueTypeInfo = valueTypeInfo;
this.jsonMap = jsonMap;
}
}
private static final String MAP_KEY_TYPE = "keyType";
private static final String MAP_VALUE_TYPE = "valueType";
private static final String MAP_VALUE = "value";
protected MapBaseIOv1() {
}
@Override
public JsonElement serialize(Map<?, ?> src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
JsonObject result = new JsonObject();
// First pass: if any entries are found, determine types of key and value.
DataTypeInfo keyInfo = DataTypeInfo.NULL;
DataTypeInfo valueInfo = DataTypeInfo.NULL;
for (Entry<?, ?> entry : src.entrySet()) {
if (keyInfo == DataTypeInfo.NULL) {
keyInfo = DataTypeInfo.forObject(entry.getKey());
}
if (valueInfo == DataTypeInfo.NULL) {
valueInfo = DataTypeInfo.forObject(entry.getValue());
}
if (keyInfo != DataTypeInfo.NULL && valueInfo != DataTypeInfo.NULL) {
break;
}
}
// Second pass: write the map entries to a JsonObject.
JsonObject dest = new JsonObject();
Type valueType = valueInfo.getType();
for (Entry<?, ?> entry : src.entrySet()) {
Object key = entry.getKey();
Object value = entry.getValue();
dest.add(key != null ? key.toString() : JsonNull.INSTANCE.toString(),
context.serialize(value, valueType));
}
// Put type information about key and value, along with the map entries
// into the resultant object.
result.add(MAP_KEY_TYPE, context.serialize(keyInfo.getTypeId()));
result.add(MAP_VALUE_TYPE, context.serialize(valueInfo.getTypeId()));
result.add(MAP_VALUE, dest);
return result;
}
public DeserializedJsonObject unpack(JsonElement jsonElement) {
if (!jsonElement.isJsonObject()) {
throw new IllegalArgumentException();
}
JsonObject object = jsonElement.getAsJsonObject();
// Unpack metadata.
JsonElement keyTypeElement = object.get(MAP_KEY_TYPE);
if (keyTypeElement == null || !keyTypeElement.isJsonPrimitive()) {
throw new IllegalArgumentException(
"Field \"" + MAP_KEY_TYPE + "\" is missing or has wrong type in Json object");
}
JsonElement valueTypeElement = object.get(MAP_VALUE_TYPE);
if (valueTypeElement == null || !valueTypeElement.isJsonPrimitive()) {
throw new IllegalArgumentException(
"Field \"" + MAP_VALUE_TYPE + "\" is missing or has wrong type in Json object");
}
DataTypeInfo keyInfo = DataTypeInfo.of(keyTypeElement.getAsString());
DataTypeInfo valueInfo = DataTypeInfo.of(valueTypeElement.getAsString());
// Unpack data.
JsonElement dataElement = object.get(MAP_VALUE);
if (dataElement == null || !dataElement.isJsonObject()) {
throw new IllegalArgumentException(
"Field \"" + MAP_VALUE + "\" is missing or has wrong type in Json object");
}
return new DeserializedJsonObject(keyInfo, valueInfo, dataElement.getAsJsonObject());
}
public static void main(String[] args) {
Map<Integer, String> map1 = new HashMap<>();
map1.put(1, "One");
map1.put(null, "Null");
Map<Integer, String> map2 = new HashMap<>();
map2.put(0, "Zero");
map2.put(2, null);
SortedMap<String, Map<Integer, String>> createdMap = new TreeMap<>();
createdMap.put("Map one", map1);
createdMap.put("Map two", map2);
Gson GSON = new GsonBuilder().serializeNulls()
.registerTypeAdapter(DataTypeInfo.of(SortedMap.class).getType(), new SortedMapIOv1())
.registerTypeAdapter(DataTypeInfo.of(Map.class).getType(), new MapIOv1())
.setPrettyPrinting().create();
String testPath = Paths.get(System.getProperty("user.home"), "Downloads").toString();
String file = Paths.get(testPath, "test-map.json").toString();
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = GSON.newJsonWriter(fileWriter)) {
GSON.toJson(createdMap, DataTypeInfo.forObject(createdMap).getType(), jsonWriter);
fileWriter.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
try (JsonReader jsonReader = GSON.newJsonReader(new FileReader(file))) {
Map<?, ?> readMap = GSON.fromJson(jsonReader, DataTypeInfo.of(Map.class).getType());
if (!readMap.equals(createdMap)) {
System.err.println("OUTPUT IS NOT EQUAL TO INPUT!!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,207 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Type;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.common.base.Preconditions;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonWriter;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.Utilities;
abstract class MapBaseIOv2 implements JsonSerializer<Map<?, ?>> {
protected static Object decodeKeyString(String keyString, DataTypeInfo typeInfo,
JsonDeserializationContext context) {
JsonElement keyElement =
keyString.equals("null") ? JsonNull.INSTANCE : new JsonPrimitive(keyString);
return context.deserialize(keyElement, typeInfo.getType());
}
static final String KEY_TYPE_KEY = "keyType";
static final String VALUE_KEY = "value";
private static final Key<?> PRIVATE_NULL_KEY = Key.of("PRIVATE NULL KEY");
protected MapBaseIOv2() {
}
@Override
public JsonElement serialize(Map<?, ?> src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
Preconditions.checkNotNull(src);
Preconditions.checkNotNull(context);
Set<?> keys = src.keySet();
// First pass: if any entries are found, determine types of key and value.
List<Object> values = new ArrayList<>();
DataTypeInfo mapKeyInfo = DataTypeInfo.NULL;
// Note that although mapValueInfo and mapValueTypeKey are very similar, there are nuances
// that lead to the need to treat them slightly differently with respect to how null values are
// handled/categorized.
DataTypeInfo mapValueInfo = DataTypeInfo.NULL; // Meaning a value is a null pointer.
Key<?> mapValueTypeKey = null; // Meaning the instance getter could not decode this.
boolean sameValueType = keys.size() > 1;
for (Object key : keys) {
DataTypeInfo keyInfo = DataTypeInfo.forObject(key);
Object value = src.get(key);
values.add(value);
if (mapKeyInfo == DataTypeInfo.NULL) {
mapKeyInfo = keyInfo;
} else if (keyInfo != DataTypeInfo.NULL && keyInfo != mapKeyInfo) {
// This could be supported if anyone ever needs it.
throw new IllegalStateException("Cannot store a key of type " + keyInfo.getTypeId()
+ " in a map inferred to have keys of type " + mapKeyInfo.getTypeId());
}
DataTypeInfo valueInfo = DataTypeInfo.forObject(value);
if (mapValueInfo == DataTypeInfo.NULL) {
mapValueInfo = valueInfo;
} else if (valueInfo != DataTypeInfo.NULL && valueInfo != mapValueInfo) {
sameValueType = false;
}
// See if there is a provider for the type indicated by this key.
Key<?> valueTypeKey = Utilities.provideTypeKeyIfPossible(value);
if (valueTypeKey == null) {
// Use a null object rather than null pointer to distinguish between the cases "no provider"
// and "no provider YET".
valueTypeKey = PRIVATE_NULL_KEY;
}
if (mapValueTypeKey == null) {
mapValueTypeKey = valueTypeKey;
} else if (mapValueTypeKey == PRIVATE_NULL_KEY || valueTypeKey == PRIVATE_NULL_KEY) {
// Use object equality for comparisons involving the private null key. This is to prevent a
// name collision should the user happen to define type key with the same name as the
// private null key.
if (mapValueTypeKey != valueTypeKey) {
sameValueType = false;
break;
}
} else if (!mapValueTypeKey.equals(valueTypeKey)) {
sameValueType = false;
}
}
// The private null key is just for use within this method, so replace it with null before
// continuing.
if (mapValueTypeKey == PRIVATE_NULL_KEY) {
mapValueTypeKey = null;
}
// Extract metadata version, if any.
Version commonVersion = IterableIOv2.getCommonVersion(values);
boolean excludeMetadataVersionInValues = commonVersion != null;
// Second pass: write the map entries to a JsonObject.
JsonObject encodedMap = new JsonObject();
for (Object metadataKey : keys) {
Object key = metadataKey != null ? metadataKey : JsonNull.INSTANCE;
Object value = src.get(metadataKey);
JsonElement encodedValue =
sameValueType ? GsonElement.encodeItem(value, excludeMetadataVersionInValues, context)
: GsonElement.encodeItemWithType(value, excludeMetadataVersionInValues, context);
encodedMap.add(key.toString(), encodedValue);
}
// Put type information about key and value, along with the map entries
// into the resultant object.
JsonObject result = new JsonObject();
result.addProperty(KEY_TYPE_KEY, mapKeyInfo.getTypeId());
if (sameValueType) {
String mapValueTypeId =
mapValueTypeKey != null ? mapValueTypeKey.getId() : mapValueInfo.getTypeId();
GsonElement.encodeTypeInfo(mapValueTypeId, result);
}
if (excludeMetadataVersionInValues) {
MetadataIOv2.encodeVersion(commonVersion, result);
}
result.add(VALUE_KEY, encodedMap);
return result;
}
protected void deserialize(JsonObject encodedItem, JsonDeserializationContext context,
Map<Object, Object> map) {
DataTypeInfo mapKeyInfo = DataTypeInfo.of(encodedItem.get(KEY_TYPE_KEY).getAsString());
Key<?> mapValueType = GsonElement.decodeTypeInfo(encodedItem);
Version commonVersion = MetadataIOv2.decodeVersion(encodedItem);
JsonObject encodedMap = encodedItem.get(VALUE_KEY).getAsJsonObject();
for (Entry<String, JsonElement> entry : encodedMap.entrySet()) {
Object key = decodeKeyString(entry.getKey(), mapKeyInfo, context);
Object value = mapValueType != null
? GsonElement.decodeItem(entry.getValue(), mapValueType, commonVersion, context)
: GsonElement.decodeItem(entry.getValue(), commonVersion, context);
map.put(key, value);
}
}
public static void main(String[] args) {
Map<Integer, String> map1 = new HashMap<>();
map1.put(1, "One");
map1.put(null, "Null");
Map<Integer, String> map2 = new HashMap<>();
map2.put(0, "Zero");
map2.put(2, null);
SortedMap<String, Map<Integer, String>> createdMap = new TreeMap<>();
createdMap.put("Map one", map1);
createdMap.put("Map two", map2);
Gson GSON = new GsonBuilder().serializeNulls()
.registerTypeAdapter(DataTypeInfo.of(SortedMap.class).getType(), new SortedMapIOv2())
.registerTypeAdapter(DataTypeInfo.of(Map.class).getType(), new MapIOv2())
.setPrettyPrinting().create();
String testPath = Paths.get(System.getProperty("user.home"), "Downloads").toString();
String file = Paths.get(testPath, "test-map.json").toString();
try (FileWriter fileWriter = new FileWriter(file)) {
try (JsonWriter jsonWriter = GSON.newJsonWriter(fileWriter)) {
GSON.toJson(createdMap, DataTypeInfo.forObject(createdMap).getType(), jsonWriter);
fileWriter.write('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
try (JsonReader jsonReader = GSON.newJsonReader(new FileReader(file))) {
Map<?, ?> readMap = GSON.fromJson(jsonReader, DataTypeInfo.of(Map.class).getType());
if (!readMap.equals(createdMap)) {
System.err.println("OUTPUT IS NOT EQUAL TO INPUT!!");
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,16 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.Map;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
interface MapIO extends JsonDeserializer<Map<?, ?>> {
@Override
Map<?, ?> deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context);
}

View File

@@ -0,0 +1,37 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
final class MapIOv1 extends MapBaseIOv1 implements MapIO {
@Override
public Map<?, ?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Map<?, ?> result = new HashMap<>();
DeserializedJsonObject object = unpack(jsonElement);
Type keyType = object.keyTypeInfo.getType();
Type valueType = object.valueTypeInfo.getType();
for (Entry<String, JsonElement> entry : object.jsonMap.getAsJsonObject().entrySet()) {
String keyString = entry.getKey();
JsonElement keyElement =
keyString.equals("null") ? JsonNull.INSTANCE : new JsonPrimitive(keyString);
JsonElement valueElement = entry.getValue();
result.put(context.deserialize(keyElement, keyType),
context.deserialize(valueElement, valueType));
}
return result;
}
}

View File

@@ -0,0 +1,25 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
final class MapIOv2 extends MapBaseIOv2 implements MapIO {
@Override
public Map<?, ?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) {
Preconditions.checkArgument(jsonElement.isJsonObject());
Map<Object, Object> result = new LinkedHashMap<>();
deserialize(jsonElement.getAsJsonObject(), context, result);
return result;
}
}

View File

@@ -0,0 +1,10 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;
import edu.jhuapl.ses.jsqrl.api.Metadata;
interface MetadataIO extends JsonSerializer<Metadata>, JsonDeserializer<Metadata> {
}

View File

@@ -0,0 +1,75 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.Map.Entry;
import com.google.common.base.Preconditions;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
final class MetadataIOv1 implements MetadataIO {
@Override
public JsonElement serialize(Metadata src, Type typeOfSrc, JsonSerializationContext context) {
Preconditions.checkNotNull(src);
Preconditions.checkArgument(DataTypeInfo.METADATA.getType().equals(typeOfSrc));
Preconditions.checkNotNull(context);
JsonArray array = new JsonArray();
array.add(context.serialize(src.getVersion(), DataTypeInfo.VERSION.getType()));
JsonObject jsonMetadata = new JsonObject();
for (Key<?> key : src.getKeys()) {
Object value = src.get(key);
jsonMetadata.add(key.getId(),
context.serialize(GsonElement.of(value), DataTypeInfo.ELEMENT.getType()));
}
array.add(jsonMetadata);
return array;
}
@Override
public Metadata deserialize(JsonElement jsonSrc, Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
Preconditions.checkNotNull(jsonSrc);
Preconditions.checkArgument(jsonSrc.isJsonArray());
Preconditions.checkArgument(DataTypeInfo.METADATA.getType().equals(typeOfT));
Preconditions.checkNotNull(context);
JsonArray jsonArray = jsonSrc.getAsJsonArray();
JsonElement jsonElement = null;
Iterator<JsonElement> iterator = jsonArray.iterator();
if (!iterator.hasNext()) {
throw new IllegalArgumentException();
}
jsonElement = iterator.next();
Version version = context.deserialize(jsonElement, DataTypeInfo.VERSION.getType());
final SettableMetadata metadata = SettableMetadata.of(version);
if (!iterator.hasNext()) {
throw new IllegalArgumentException();
}
jsonElement = iterator.next();
JsonObject jsonMetadata = jsonElement.getAsJsonObject();
for (Entry<String, JsonElement> entry : jsonMetadata.entrySet()) {
Key<Object> key = Key.of(entry.getKey());
GsonElement element = context.deserialize(entry.getValue(), DataTypeInfo.ELEMENT.getType());
metadata.put(key, element.getValue());
}
return metadata;
}
}

View File

@@ -0,0 +1,159 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
final class MetadataIOv2 implements MetadataIO {
private static final String VERSION_KEY = "metadata.Version";
public static JsonObject encodeWithoutVersion(Metadata src, JsonSerializationContext context) {
JsonObject result = encodeWithVersion(src, context);
result.remove(DataTypeInfo.VERSION.getTypeId());
return result;
}
public static JsonObject encodeWithVersion(Metadata src, JsonSerializationContext context) {
Map<String, Object> map = new LinkedHashMap<>();
for (Key<?> key : src.getKeys()) {
map.put(key.getId(), src.get(key));
}
// Use MapIO to do the detailed encoding.
MapIOv2 mapIo = new MapIOv2();
JsonObject encodedMap =
mapIo.serialize(map, DataTypeInfo.MAP.getType(), context).getAsJsonObject();
// Pull out info needed to characterize the Metadata. (Skip the Key type -- known to be string).
JsonObject result = new JsonObject();
result.add(DataTypeInfo.VERSION.getTypeId(), GsonVersionIO.encode(src.getVersion()));
if (encodedMap.has(GsonElement.VALUE_TYPE_KEY)) {
result.add(GsonElement.VALUE_TYPE_KEY, encodedMap.get(GsonElement.VALUE_TYPE_KEY));
}
if (encodedMap.has(VERSION_KEY)) {
result.add(VERSION_KEY, encodedMap.get(VERSION_KEY));
}
// Rebundle the serialized map so it's stored in the same order as the metadata.
JsonObject keyValueObject = encodedMap.get(MapBaseIOv2.VALUE_KEY).getAsJsonObject();
JsonObject keyValueObjectInOrder = new JsonObject();
for (Key<?> key : src.getKeys()) {
String keyId = key.getId();
keyValueObjectInOrder.add(keyId, keyValueObject.get(keyId));
}
result.add(MapBaseIOv2.VALUE_KEY, keyValueObjectInOrder);
return result;
}
public static void encodeVersion(Version version, JsonObject object) {
object.add(VERSION_KEY, GsonVersionIO.encode(version));
}
public static Metadata decode(JsonObject jsonMetadata, Version version,
JsonDeserializationContext context) {
Preconditions.checkNotNull(jsonMetadata);
Preconditions.checkNotNull(version);
Preconditions.checkNotNull(context);
// Reverse the encoding process. The ordered map is stored as the "value".
JsonObject keyValueObjectInOrder = jsonMetadata.get(MapBaseIOv2.VALUE_KEY).getAsJsonObject();
// Convert the supplied jsonMetadata object into the same format as an encoded map.
JsonObject encodedMap = new JsonObject();
// Note the key type was not serialized but we need to add it here.
encodedMap.addProperty(MapBaseIOv2.KEY_TYPE_KEY, DataTypeInfo.STRING.getTypeId());
if (jsonMetadata.has(GsonElement.VALUE_TYPE_KEY)) {
encodedMap.add(GsonElement.VALUE_TYPE_KEY, jsonMetadata.get(GsonElement.VALUE_TYPE_KEY));
}
if (jsonMetadata.has(VERSION_KEY)) {
encodedMap.add(VERSION_KEY, jsonMetadata.get(VERSION_KEY));
}
encodedMap.add(MapBaseIOv2.VALUE_KEY, keyValueObjectInOrder);
// Now decode the synthesized encoded map.
MapIOv2 mapIo = new MapIOv2();
@SuppressWarnings("unchecked")
Map<String, Object> map =
(Map<String, Object>) mapIo.deserialize(encodedMap, DataTypeInfo.MAP.getType(), context);
// Finally use the key order from the serialized form to reconstruct the metadata object content
// in order.
SettableMetadata metadata = SettableMetadata.of(version);
for (Entry<String, JsonElement> entry : keyValueObjectInOrder.entrySet()) {
String keyId = entry.getKey();
// Pull the reconstructed elements of the metadata from the map.
metadata.put(Key.of(keyId), map.get(keyId));
}
return metadata;
}
public static Metadata decodeWithVersion(JsonObject jsonMetadata,
JsonDeserializationContext context) {
Preconditions.checkNotNull(jsonMetadata);
Version version = decodeVersion(jsonMetadata);
return decode(jsonMetadata, version, context);
}
public static Version decodeVersion(JsonObject object) {
Version result = null;
if (object.has(VERSION_KEY)) {
result = GsonVersionIO.decode(object.get(VERSION_KEY).getAsJsonPrimitive());
} else if (object.has(DataTypeInfo.VERSION.getTypeId())) {
result =
GsonVersionIO.decode(object.get(DataTypeInfo.VERSION.getTypeId()).getAsJsonPrimitive());
}
return result;
}
/*
* (non-Javadoc)
*
* @see
* edu.jhuapl.ses.jsqrl.impl.gson.MetadataIO#serialize(edu.jhuapl.ses.jsqrl.api.Metadata,
* java.lang.reflect.Type, com.google.gson.JsonSerializationContext)
*/
@Override
public JsonElement serialize(Metadata src, Type typeOfSrc, JsonSerializationContext context) {
Preconditions.checkArgument(DataTypeInfo.METADATA.getType().equals(typeOfSrc));
return encodeWithVersion(src, context);
}
/*
* (non-Javadoc)
*
* @see edu.jhuapl.ses.jsqrl.impl.gson.MetadataIO#deserialize(com.google.gson.JsonElement,
* java.lang.reflect.Type, com.google.gson.JsonDeserializationContext)
*/
@Override
public Metadata deserialize(JsonElement jsonSrc, Type typeOfT,
JsonDeserializationContext context) {
Preconditions.checkNotNull(jsonSrc);
Preconditions.checkArgument(jsonSrc.isJsonObject());
Preconditions.checkArgument(DataTypeInfo.METADATA.getType().equals(typeOfT));
return decodeWithVersion(jsonSrc.getAsJsonObject(), context);
}
}

View File

@@ -0,0 +1,8 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonSerializer;
interface ProxyIO<T> extends JsonSerializer<Object>, JsonDeserializer<T> {
}

View File

@@ -0,0 +1,57 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.StorableAsMetadata;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
final class ProxyIOv1<T> implements ProxyIO<T> {
@Override
public JsonElement serialize(Object src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
JsonObject object = new JsonObject();
Key<?> key;
Metadata metadata;
if (src instanceof StorableAsMetadata) {
StorableAsMetadata<?> storable = (StorableAsMetadata<?>) src;
key = storable.getKey();
metadata = storable.store();
} else {
@SuppressWarnings("unchecked")
Class<Object> objectType = (Class<Object>) src.getClass();
InstanceGetter instanceGetter = InstanceGetter.defaultInstanceGetter();
key = instanceGetter.getKeyForType(objectType);
metadata = instanceGetter.providesMetadataFromGenericObject(objectType).provide(src);
}
object.addProperty("proxiedType", key.getId());
object.add("proxyMetadata", context.serialize(metadata, DataTypeInfo.METADATA.getType()));
return object;
}
@Override
public T deserialize(JsonElement json, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
Key<T> proxyKey = Key.of(object.get("proxiedType").getAsString());
Metadata objectMetadata =
context.deserialize(object.get("proxyMetadata"), DataTypeInfo.METADATA.getType());
return InstanceGetter.defaultInstanceGetter().providesGenericObjectFromMetadata(proxyKey)
.provide(objectMetadata);
}
}

View File

@@ -0,0 +1,57 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSerializationContext;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.StorableAsMetadata;
import edu.jhuapl.ses.jsqrl.impl.InstanceGetter;
final class ProxyIOv2<T> implements ProxyIO<T> {
@Override
public JsonElement serialize(Object src, @SuppressWarnings("unused") Type typeOfSrc,
JsonSerializationContext context) {
JsonObject object = new JsonObject();
Key<?> key;
Metadata metadata;
if (src instanceof StorableAsMetadata) {
StorableAsMetadata<?> storable = (StorableAsMetadata<?>) src;
key = storable.getKey();
metadata = storable.store();
} else {
@SuppressWarnings("unchecked")
Class<Object> objectType = (Class<Object>) src.getClass();
InstanceGetter instanceGetter = InstanceGetter.defaultInstanceGetter();
key = instanceGetter.getKeyForType(objectType);
metadata = instanceGetter.providesMetadataFromGenericObject(objectType).provide(src);
}
object.addProperty("proxiedType", key.getId());
object.add("proxyMetadata", context.serialize(metadata, DataTypeInfo.METADATA.getType()));
return object;
}
@Override
public T deserialize(JsonElement json, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonObject object = json.getAsJsonObject();
Key<T> proxyKey = Key.of(object.get("proxiedType").getAsString());
Metadata objectMetadata =
context.deserialize(object.get("proxyMetadata"), DataTypeInfo.METADATA.getType());
return InstanceGetter.defaultInstanceGetter().providesGenericObjectFromMetadata(proxyKey)
.provide(objectMetadata);
}
}

View File

@@ -0,0 +1,66 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Type;
import java.util.Base64;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
public class SerializableIO
implements JsonSerializer<Serializable>, JsonDeserializer<Serializable> {
public SerializableIO() {
super();
}
@Override
public JsonElement serialize(Serializable src, Type typeOfSrc, JsonSerializationContext context) {
String encodedString;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
try (ObjectOutputStream oos =
new ObjectOutputStream(/* new GZIPOutputStream( */new GZIPOutputStream(baos))) {
oos.writeObject(src);
}
encodedString = Base64.getEncoder().encodeToString(baos.toByteArray());
} catch (Exception e) {
encodedString = null;
}
return encodedString != null ? new JsonPrimitive(encodedString) : JsonNull.INSTANCE;
}
@Override
public Serializable deserialize(JsonElement jsonElement, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
Preconditions.checkArgument(jsonElement.isJsonPrimitive());
String encodedString = jsonElement.getAsString();
byte[] data = Base64.getDecoder().decode(encodedString);
Serializable serializable;
try (ObjectInputStream ois =
new ObjectInputStream(new GZIPInputStream(new ByteArrayInputStream(data)))) {
serializable = (Serializable) ois.readObject();
} catch (Exception e) {
serializable = null;
}
return serializable;
}
}

View File

@@ -0,0 +1,102 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.io.File;
import java.io.IOException;
import com.google.common.base.Preconditions;
import edu.jhuapl.ses.jsqrl.api.Key;
import edu.jhuapl.ses.jsqrl.api.Metadata;
import edu.jhuapl.ses.jsqrl.api.MetadataManager;
import edu.jhuapl.ses.jsqrl.api.Serializer;
import edu.jhuapl.ses.jsqrl.api.Version;
import edu.jhuapl.ses.jsqrl.impl.FixedMetadata;
import edu.jhuapl.ses.jsqrl.impl.SettableMetadata;
import edu.jhuapl.ses.jsqrl.impl.gson.GsonSerializer;
public class Serializers {
private static final Serializer INSTANCE = GsonSerializer.of();
public static Serializer getDefault() {
return INSTANCE;
}
public static Serializer of() {
return GsonSerializer.of();
}
public static void serialize(String metadataId, MetadataManager manager, File file)
throws IOException {
Preconditions.checkNotNull(metadataId);
Preconditions.checkNotNull(manager);
Preconditions.checkNotNull(file);
Serializer serializer = of();
serializer.register(Key.of(metadataId), manager);
serializer.save(file);
}
public static void serialize(String metadataId, Metadata metadata, File file) throws IOException {
Preconditions.checkNotNull(metadataId);
Preconditions.checkNotNull(metadata);
Preconditions.checkNotNull(file);
serialize(metadataId, new MetadataManager() {
@Override
public Metadata store() {
return metadata;
}
@Override
public void retrieve(@SuppressWarnings("unused") Metadata source) {
throw new UnsupportedOperationException();
}
}, file);
}
public static void deserialize(File file, String metadataId, MetadataManager manager)
throws IOException {
Preconditions.checkNotNull(file);
Preconditions.checkNotNull(metadataId);
Preconditions.checkNotNull(manager);
Serializer serializer = of();
Key<Metadata> key = Key.of(metadataId);
serializer.register(key, manager);
serializer.load(file);
serializer.deregister(key);
}
public static FixedMetadata deserialize(File file, String metadataId) throws IOException {
Preconditions.checkNotNull(file);
Preconditions.checkNotNull(metadataId);
class DirectMetadataManager implements MetadataManager {
final SettableMetadata metadata = SettableMetadata.of(Version.of(0, 1));
@Override
public Metadata store() {
throw new UnsupportedOperationException();
}
@Override
public void retrieve(Metadata source) {
for (Key<?> key : source.getKeys()) {
put(key, source.get(key), metadata);
}
}
}
DirectMetadataManager manager = new DirectMetadataManager();
deserialize(file, metadataId, manager);
return FixedMetadata.of(manager.metadata);
}
@SuppressWarnings("unchecked")
private static <T> void put(Key<?> key, T object, SettableMetadata metadata) {
metadata.put((Key<T>) key, object);
}
}

View File

@@ -0,0 +1,9 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.util.Set;
import com.google.gson.JsonDeserializer;
interface SetIO extends JsonDeserializer<Set<?>> {
}

View File

@@ -0,0 +1,30 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Set;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
final class SetIOv1 extends IterableIOv1 implements SetIO {
@Override
public Set<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
DeserializedJsonArray array = unpack(jsonElement);
DataTypeInfo dataInfo = array.dataTypeInfo;
JsonArray jsonArray = array.jsonArray;
// Create output data object.
Set<?> result = new HashSet<>();
Type valueType = dataInfo.getType();
for (JsonElement entryElement : jsonArray) {
result.add(context.deserialize(entryElement, valueType));
}
return result;
}
}

View File

@@ -0,0 +1,21 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.LinkedHashSet;
import java.util.Set;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
final class SetIOv2 extends IterableIOv2 implements SetIO {
@Override
public Set<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) {
Preconditions.checkArgument(jsonElement.isJsonObject());
return new LinkedHashSet<>(deserialize(jsonElement.getAsJsonObject(), context));
}
}

View File

@@ -0,0 +1,10 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.util.SortedMap;
import com.google.gson.JsonDeserializer;
interface SortedMapIO extends JsonDeserializer<SortedMap<?, ?>> {
}

View File

@@ -0,0 +1,38 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import com.google.gson.JsonParseException;
import com.google.gson.JsonPrimitive;
final class SortedMapIOv1 extends MapBaseIOv1 implements SortedMapIO {
@Override
public SortedMap<?, ?> deserialize(JsonElement jsonElement,
@SuppressWarnings("unused") Type typeOfT, JsonDeserializationContext context)
throws JsonParseException {
SortedMap<?, ?> result = new TreeMap<>();
DeserializedJsonObject object = unpack(jsonElement);
Type keyType = object.keyTypeInfo.getType();
Type valueType = object.valueTypeInfo.getType();
for (Entry<String, JsonElement> entry : object.jsonMap.getAsJsonObject().entrySet()) {
String keyString = entry.getKey();
JsonElement keyElement =
keyString.equals("null") ? JsonNull.INSTANCE : new JsonPrimitive(keyString);
JsonElement valueElement = entry.getValue();
result.put(context.deserialize(keyElement, keyType),
context.deserialize(valueElement, valueType));
}
return result;
}
}

View File

@@ -0,0 +1,25 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.SortedMap;
import java.util.TreeMap;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
final class SortedMapIOv2 extends MapBaseIOv2 implements SortedMapIO {
@Override
public SortedMap<?, ?> deserialize(JsonElement jsonElement,
@SuppressWarnings("unused") Type typeOfT, JsonDeserializationContext context) {
Preconditions.checkArgument(jsonElement.isJsonObject());
SortedMap<Object, Object> result = new TreeMap<>();
deserialize(jsonElement.getAsJsonObject(), context, result);
return result;
}
}

View File

@@ -0,0 +1,10 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.util.SortedSet;
import com.google.gson.JsonDeserializer;
interface SortedSetIO extends JsonDeserializer<SortedSet<?>> {
}

View File

@@ -0,0 +1,30 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.SortedSet;
import java.util.TreeSet;
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
final class SortedSetIOv1 extends IterableIOv1 implements SortedSetIO {
@Override
public SortedSet<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
DeserializedJsonArray array = unpack(jsonElement);
DataTypeInfo dataInfo = array.dataTypeInfo;
JsonArray jsonArray = array.jsonArray;
// Create output data object.
SortedSet<?> result = new TreeSet<>();
Type valueType = dataInfo.getType();
for (JsonElement entryElement : jsonArray) {
result.add(context.deserialize(entryElement, valueType));
}
return result;
}
}

View File

@@ -0,0 +1,21 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
import java.lang.reflect.Type;
import java.util.SortedSet;
import java.util.TreeSet;
import com.google.common.base.Preconditions;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
final class SortedSetIOv2 extends IterableIOv2 implements SortedSetIO {
@Override
public SortedSet<?> deserialize(JsonElement jsonElement, @SuppressWarnings("unused") Type typeOfT,
JsonDeserializationContext context) {
Preconditions.checkArgument(jsonElement.isJsonObject());
return new TreeSet<>(deserialize(jsonElement.getAsJsonObject(), context));
}
}

View File

@@ -0,0 +1,6 @@
package edu.jhuapl.ses.jsqrl.impl.gson;
public class Spud
{
}

View File

@@ -0,0 +1,43 @@
/**
* Core implementation of the metadata API. This includes factories for implementations of the
* primary interfaces (Metadata etc.) as well as utility classes and abstract base implementations.
* <p>
* Release Notes
* <p>
* Package Version 4.4.1, 2020-03-11
* <p>
* 1. Fixed a bug which caused SortedMap and SortedSet implementations to be serialized as normal
* Maps and Sets, respectively. This led to spurious exceptions when they were deserialized.
* <p>
* Package Version 4.4, 2020-03-05
* <p>
* 1. Support storing/retrieving Class objects.
* <p>
* 2. Use LinkedHashSet and LinkedHashMap to do a better job of preserving order.
* <p>
* Package Version 4.3, 2019-11-15
* <p>
* Bug fix: if InstanceGetter had a custom serializer/deserializer for a type that implemented Map,
* Set or List, two bugs would cause an object of that type to be serialized as a Map, Set or List,
* bypassing the custom serializer.
* <p>
* Package Version 4.2, 2019-10-02
* <p>
* Bug fix: when serializing Iterables and Maps, under some circumstances, objects that should have
* been recognized as having different types were being treated as if they were stored the same way.
* This resulted in errors when trying to deserialize metadata files that had been written by the
* buggy code.
* <p>
* Package Version 4.1, 2019-04-10
* <p>
* InstanceGetter: Add support for matching abstract types and interfaces when encoding/decoding
* objects as proxy metadata.
* <p>
* Package Version 4.0, 2019-03-25
* <p>
* 1. Accept, serialize and deserialize Class<?> objects.
* <p>
* 2. Create directories as needed to serialize the file in question.
* <p>
*/
package edu.jhuapl.ses.jsqrl;