diff --git a/Dockerfile b/Dockerfile index 7d88a608d..e475ff340 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,9 +26,11 @@ COPY --from=source /src/.docker/config /app/config WORKDIR /app +ENV AMY_SOCKET_CONFIG_WEB_SOCKET_PORT 6661 ENV AMY_SERVER_CONFIG_SERVER_SOCKET_PORT 80 ENV AMY_SERVER_CONFIG_SERVER_URL http://localhost:80/ EXPOSE $AMY_SERVER_CONFIG_SERVER_SOCKET_PORT +EXPOSE $AMY_SOCKET_CONFIG_WEB_SOCKET_PORT RUN mkdir /config RUN mkdir /data diff --git a/README.md b/README.md index 9d2d8240e..6fb7caf44 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=de.unistuttgart.iaas.amyassist%3Aamy&metric=bugs)](https://sonarcloud.io/component_measures?id=de.unistuttgart.iaas.amyassist%3Aamy&metric=Reliability) [![Reliability](https://sonarcloud.io/api/project_badges/measure?project=de.unistuttgart.iaas.amyassist%3Aamy&metric=reliability_rating)](https://sonarcloud.io/component_measures?id=de.unistuttgart.iaas.amyassist%3Aamy&metric=Reliability) +[![Docker](http://dockeri.co/image/amyassist/amy)](https://hub.docker.com/r/amyassist/amy/) + + ### Personal Assistance System This is a research project from students of the University of Stuttgart. No functionality is tested. There may be harmful errors. @@ -76,7 +79,9 @@ Direct links: ## Deployment -TODO +Best use our docker compose [project](https://github.com/amyassist/amy-all). +Otherwise build the master node as a jar and every plugin as a jar and place all plugin jars in a directory. +Then create a directory named config and place all required configs in there. In the plugin.config set the plugin path acordingly. ## Built With @@ -88,7 +93,7 @@ Please read [CONTRIBUTING.md](CONTRIBUTING.md) for details on our code of conduc ## Versioning -TODO +This project uses [Semantic Versioning](https://semver.org/). ## Authors diff --git a/amy-master-node/pom.xml b/amy-master-node/pom.xml index 0cbfefa24..5d88dcc9e 100644 --- a/amy-master-node/pom.xml +++ b/amy-master-node/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml @@ -55,6 +55,11 @@ amy-natlang ${amy.natlang.version} + + de.unistuttgart.iaas.amyassist + amy-chat-socket + ${amy.chatsocket.version} + diff --git a/amy-message-hub-api/pom.xml b/amy-message-hub-api/pom.xml index d9e96487a..b073512f7 100644 --- a/amy-message-hub-api/pom.xml +++ b/amy-message-hub-api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/amy-message-hub/pom.xml b/amy-message-hub/pom.xml index 240de58bb..29b70d9fe 100644 --- a/amy-message-hub/pom.xml +++ b/amy-message-hub/pom.xml @@ -5,7 +5,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 amy-message-hub diff --git a/api/pom.xml b/api/pom.xml index bd330c138..7f5c066c0 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/api/TaskScheduler.java b/api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/api/TaskScheduler.java index 3ecad8b78..c5679eaca 100644 --- a/api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/api/TaskScheduler.java +++ b/api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/api/TaskScheduler.java @@ -24,6 +24,10 @@ package de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api; import java.time.Instant; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.annotation.Nonnull; /** * Service to schedule and execute concurrent tasks. @@ -46,6 +50,22 @@ public interface TaskScheduler { * the task to execute * @param instant * The instant at which to execute that task + * @return the reference to the scheduled task + */ + @Nonnull + ScheduledFuture schedule(Runnable task, Instant instant); + + /** + * Schedules the given task to execute at the given delay + * + * @param task + * the task to execute + * @param delay + * the delay + * @param timeUnit + * the time unit of the delay + * @return the reference to the scheduled task */ - void schedule(Runnable task, Instant instant); + @Nonnull + ScheduledFuture schedule(Runnable task, long delay, TimeUnit timeUnit); } diff --git a/chat-socket/.settings/org.eclipse.jdt.core.prefs b/chat-socket/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/chat-socket/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/chat-socket/.settings/org.eclipse.jdt.ui.prefs b/chat-socket/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/chat-socket/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/chat-socket/.settings/org.eclipse.m2e.core.prefs b/chat-socket/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/chat-socket/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/chat-socket/pom.xml b/chat-socket/pom.xml new file mode 100644 index 000000000..11b2b258f --- /dev/null +++ b/chat-socket/pom.xml @@ -0,0 +1,57 @@ + + 4.0.0 + amy-chat-socket + Amy chat socket + + de.unistuttgart.iaas.amyassist + amy + 0.6.0 + ../pom.xml + + + + org.slf4j + slf4j-api + + + de.unistuttgart.iaas.amyassist + amy-natlang-api + + + de.unistuttgart.iaas.amyassist + amy-di-api + + + de.unistuttgart.iaas.amyassist + amy-api + + + org.java-websocket + Java-WebSocket + 1.3.9 + + + com.fasterxml.jackson.core + jackson-core + 2.8.10 + + + com.fasterxml.jackson.core + jackson-databind + 2.8.10 + + + com.fasterxml.jackson.module + jackson-module-jaxb-annotations + 2.9.5 + compile + + + javax.ws.rs + javax.ws.rs-api + + + Socket for chat communication + diff --git a/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatConfig.java b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatConfig.java new file mode 100644 index 000000000..12e69209a --- /dev/null +++ b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatConfig.java @@ -0,0 +1,53 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.socket; + +import de.unistuttgart.iaas.amyassist.amy.core.configuration.ConfigurationManager; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; + +import java.util.Properties; + +/** + * Config helper class for websocket server + * + * @author Benno Krauß + */ +@Service +public class ChatConfig { + /** The name of the config used by this class */ + private static final String CONFIG_NAME = "socket.config"; + /** The name of the property, which specifies the port */ + static final String PROPERTY_PORT = "webSocketPort"; + /** The name of the property which specifies the websocket endpoint URL */ + static final String WEBSOCKET_URL = "webSocketURL"; + + @Reference + private ConfigurationManager configurationManager; + + String get(String key) { + Properties conf = this.configurationManager.getConfigurationWithDefaults(CONFIG_NAME); + return conf.getProperty(key); + } +} diff --git a/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatServer.java b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatServer.java new file mode 100644 index 000000000..b15181625 --- /dev/null +++ b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatServer.java @@ -0,0 +1,84 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.socket; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.DialogHandler; +import de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService; +import org.slf4j.Logger; + +import java.io.IOException; + +import static de.unistuttgart.iaas.amyassist.amy.socket.ChatConfig.PROPERTY_PORT; + +/** + * the class running the server socket for the chat + * + * @author Christian Bräuner + */ +@Service(ChatServer.class) +public class ChatServer implements RunnableService { + + @Reference + private ChatConfig config; + + @Reference + private Logger logger; + + @Reference + private DialogHandler handler; + + private ChatWebSocket socket; + + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#start() + */ + @Override + public void start() { + + int port = Integer.parseInt(config.get(PROPERTY_PORT)); + this.socket = new ChatWebSocket(port, this.handler); + this.socket.start(); + + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#stop() + */ + @Override + public void stop() { + try { + this.socket.stop(); + } catch (IOException e) { + this.logger.error("Can't close chatserver", e); + } catch (InterruptedException e) { + this.logger.error("Interrupt while stoping", e); + Thread.currentThread().interrupt(); + } + this.logger.info("ChatServer shutdown"); + } + +} diff --git a/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatWebSocket.java b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatWebSocket.java new file mode 100644 index 000000000..e8b94deef --- /dev/null +++ b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/ChatWebSocket.java @@ -0,0 +1,132 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.socket; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.AnnotationIntrospector; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector; +import com.fasterxml.jackson.databind.type.TypeFactory; +import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.DialogHandler; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; +import org.java_websocket.WebSocket; +import org.java_websocket.handshake.ClientHandshake; +import org.java_websocket.server.WebSocketServer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.net.InetSocketAddress; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * A web socket for the chat communication + * + * @author Christian Bräuner, Benno Krauß + */ +public class ChatWebSocket extends WebSocketServer { + + private Logger logger = LoggerFactory.getLogger(ChatWebSocket.class); + + private DialogHandler handler; + + private Map dialogMap = new HashMap<>(); + + private ObjectMapper mapper; + + /** + *creates a new web socket server + * + *@param port the port of the server + *@param handler the dialog handler of the backend + * + */ + public ChatWebSocket(int port, DialogHandler handler) { + super(new InetSocketAddress(port)); + this.handler = handler; + + AnnotationIntrospector firstInspector = new JacksonAnnotationIntrospector(); + AnnotationIntrospector secondInspector = new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()); + AnnotationIntrospector inspectors = AnnotationIntrospector.pair(firstInspector, secondInspector); + + mapper = new ObjectMapper(); + mapper.setAnnotationIntrospector(inspectors); + } + + /** + * @see org.java_websocket.server.WebSocketServer#onClose(org.java_websocket.WebSocket, int, java.lang.String, boolean) + */ + @Override + public void onClose(WebSocket clientSocket, int code, String reason, boolean remote) { + this.logger.debug("{} closed connection", clientSocket); + this.logger.debug("Code was {}. Reason was {}.", code, reason); + + } + + /** + * @see org.java_websocket.server.WebSocketServer#onError(org.java_websocket.WebSocket, java.lang.Exception) + */ + @Override + public void onError(WebSocket conn, Exception ex) { + this.logger.error("Web Socket exception", ex); + } + + /** + * @see org.java_websocket.server.WebSocketServer#onMessage(org.java_websocket.WebSocket, java.lang.String) + */ + @Override + public void onMessage(WebSocket conn, String message) { + this.handler.process(message, this.dialogMap.get(conn.getRemoteSocketAddress())); + } + + /** + * @see org.java_websocket.server.WebSocketServer#onOpen(org.java_websocket.WebSocket, org.java_websocket.handshake.ClientHandshake) + */ + @Override + public void onOpen(WebSocket conn, ClientHandshake handshake) { + send(conn, Response.text("Hello, I am Amy").build()); + UUID uuid = this.handler.createDialog(msg -> send(conn, msg)); + this.dialogMap.put(conn.getRemoteSocketAddress(), uuid); + } + + private void send(WebSocket socket, Response response) { + try { + socket.send(mapper.writeValueAsString(response)); + } catch (JsonProcessingException e) { + logger.error("Error serializing Response-object", e); + } + } + + /** + * @see org.java_websocket.server.WebSocketServer#onStart() + */ + @Override + public void onStart() { + this.logger.info("Chatserver started"); + setConnectionLostTimeout(100); + } + +} diff --git a/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/RestResource.java b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/RestResource.java new file mode 100644 index 000000000..e909ca529 --- /dev/null +++ b/chat-socket/src/main/java/de/unistuttgart/iaas/amyassist/amy/socket/RestResource.java @@ -0,0 +1,52 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.socket; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; + +import static de.unistuttgart.iaas.amyassist.amy.socket.ChatConfig.WEBSOCKET_URL; +import static javax.ws.rs.core.MediaType.TEXT_PLAIN; + +/** + * Rest class to provide the web client the websocket endpoint URL + * + * @author Benno Krauß + */ +@Path("chat") +public class RestResource { + + @Reference + ChatConfig config; + + @Path("url") + @Produces(TEXT_PLAIN) + @GET + public String getChatURL() { + return config.get(WEBSOCKET_URL); + } +} diff --git a/chat-socket/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/chat-socket/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..f18ef90ab --- /dev/null +++ b/chat-socket/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1,2 @@ +de.unistuttgart.iaas.amyassist.amy.socket.ChatServer +de.unistuttgart.iaas.amyassist.amy.socket.ChatConfig diff --git a/chat-socket/src/main/resources/META-INF/javax.ws.rs.Path b/chat-socket/src/main/resources/META-INF/javax.ws.rs.Path new file mode 100644 index 000000000..b531cea18 --- /dev/null +++ b/chat-socket/src/main/resources/META-INF/javax.ws.rs.Path @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.socket.RestResource diff --git a/chat-socket/src/main/resources/META-INF/socket.config.properties b/chat-socket/src/main/resources/META-INF/socket.config.properties new file mode 100644 index 000000000..60e907346 --- /dev/null +++ b/chat-socket/src/main/resources/META-INF/socket.config.properties @@ -0,0 +1,2 @@ +webSocketPort=6661 +webSocketURL=ws://localhost:6661/ diff --git a/core/pom.xml b/core/pom.xml index 1c86536db..650b94636 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -9,7 +9,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml @@ -145,7 +145,7 @@ de.unistuttgart.iaas.amyassist amy-remote-sr - 0.5.0 + 0.6.0 diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/Core.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/Core.java index 693fca29e..164a9ff19 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/Core.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/Core.java @@ -28,12 +28,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.unistuttgart.iaas.amyassist.amy.core.console.ExitConsole; import de.unistuttgart.iaas.amyassist.amy.core.di.Context; import de.unistuttgart.iaas.amyassist.amy.core.di.DependencyInjection; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumerImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.SingletonServiceProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.io.CommandLineArgumentHandlerService; import de.unistuttgart.iaas.amyassist.amy.core.io.CommandLineArgumentInfo; import de.unistuttgart.iaas.amyassist.amy.core.pluginloader.PluginManager; @@ -48,6 +47,9 @@ */ public class Core { + private static final int EXIT_CODE_ALL_GOOD = 0; + private static final int EXIT_CODE_CMA_FLAGS_INVALID = 11; + private final Logger logger = LoggerFactory.getLogger(Core.class); private final DependencyInjection di; @@ -76,12 +78,17 @@ public Core() { void start(String[] args) { this.registerAllCoreServices(); this.init(); - CommandLineArgumentHandlerService cmaHandler = this.di.getService(new ServiceConsumerImpl<>(this.getClass(), - new ServiceDescriptionImpl<>(CommandLineArgumentHandlerService.class))).getService(); + CommandLineArgumentHandlerService cmaHandler = this.di.getServiceLocator() + .getService(new ServiceConsumerImpl<>(this.getClass(), + new ServiceDescriptionImpl<>(CommandLineArgumentHandlerService.class))) + .getService(); cmaHandler.load(args, System.out::println); if (cmaHandler.shouldProgramContinue()) { - this.di.register(new SingletonServiceProvider<>(CommandLineArgumentInfo.class, cmaHandler.getInfo())); + this.di.getConfiguration() + .register(new SingletonServiceProvider<>(CommandLineArgumentInfo.class, cmaHandler.getInfo())); this.run(); + } else { + System.exit(cmaHandler.areFlagsValid() ? EXIT_CODE_ALL_GOOD : EXIT_CODE_CMA_FLAGS_INVALID); // NOSONAR } } @@ -100,8 +107,6 @@ private void run() { * register all instances and classes in the DI */ private void registerAllCoreServices() { - this.di.register(new SingletonServiceProvider<>(Core.class, this)); - this.di.loadServices(); } @@ -113,7 +118,7 @@ private void init() { } private void loadPlugins() { - PluginManager pluginManager = this.di + PluginManager pluginManager = this.di.getServiceLocator() .getService( new ServiceConsumerImpl<>(this.getClass(), new ServiceDescriptionImpl<>(PluginManager.class))) .getService(); @@ -122,7 +127,8 @@ private void loadPlugins() { } catch (IOException e) { throw new IllegalStateException("Could not load plugins due to an IOException.", e); } - this.di.registerContextProvider(Context.PLUGIN, new PluginProvider(pluginManager.getPlugins())); + this.di.getConfiguration().registerContextProvider(Context.PLUGIN, + new PluginProvider(pluginManager.getPlugins())); } /** @@ -138,18 +144,9 @@ private void start() { Runtime.getRuntime().addShutdownHook(this.shutdownHook); } - /** - * stop all Threads and terminate the application. This is call form the {@link ExitConsole} - */ - public void stop() { - Runtime.getRuntime().removeShutdownHook(this.shutdownHook); - this.doStop(); - } - private void doStop() { this.logger.info("stop"); this.runnableServiceExtension.stop(); - this.di.shutdown(); this.logger.info("stopped"); } diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/configuration/PropertiesProvider.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/configuration/PropertiesProvider.java index 3bbf51e1a..535abf11a 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/configuration/PropertiesProvider.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/configuration/PropertiesProvider.java @@ -32,10 +32,9 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.*; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ConsumerFactory; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandleImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceImplementationDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.pluginloader.IPlugin; /** @@ -53,35 +52,36 @@ public class PropertiesProvider implements ServiceProvider { } @Override - public ServiceImplementationDescription getServiceImplementationDescription( + public ServiceInstantiationDescription getServiceInstantiationDescription( @Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { Map context = new HashMap<>(); context.put(Context.PLUGIN, locator.getContextProvider(Context.PLUGIN).getContext(serviceConsumer)); context.put(CONTEXT_WITH_DEFAULT, serviceConsumer.getServiceDescription().getAnnotations().stream() .anyMatch(a -> a instanceof WithDefault)); - return new ServiceImplementationDescriptionImpl<>(serviceConsumer.getServiceDescription(), context, + return new ServiceInstantiationDescriptionImpl<>(serviceConsumer.getServiceDescription(), context, Properties.class); } @Override - public @Nonnull ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { + public @Nonnull Properties createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { ConfigurationManager configurationLoader = locator.getService(ConsumerFactory.build(PropertiesProvider.class, new ServiceDescriptionImpl<>(ConfigurationManager.class))).getService(); - IPlugin plugin = (IPlugin) serviceImplementationDescription.getContext().get(Context.PLUGIN); + IPlugin plugin = (IPlugin) serviceInstantiationDescription.getContext().get(Context.PLUGIN); String uniqueName = plugin.getUniqueName(); - boolean withDefault = (boolean) serviceImplementationDescription.getContext().get(CONTEXT_WITH_DEFAULT); + boolean withDefault = (boolean) serviceInstantiationDescription.getContext().get(CONTEXT_WITH_DEFAULT); if (withDefault) { ClassLoader classLoader = plugin.getClassLoader(); - return new ServiceHandleImpl<>(configurationLoader.getConfigurationWithDefaults(uniqueName, classLoader)); + return configurationLoader.getConfigurationWithDefaults(uniqueName, classLoader); } - return new ServiceHandleImpl<>(configurationLoader.getConfiguration(uniqueName)); + return configurationLoader.getConfiguration(uniqueName); } @Override - public void dispose(ServiceHandle properties) { - // TODO maybe save the properties + public void dispose(@Nonnull Properties properties, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + // nothing to do here } } diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/console/ExitConsole.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/console/ExitConsole.java index cdff3a016..fcf32a9fa 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/console/ExitConsole.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/console/ExitConsole.java @@ -24,8 +24,6 @@ package de.unistuttgart.iaas.amyassist.amy.core.console; import asg.cliche.Command; -import de.unistuttgart.iaas.amyassist.amy.core.Core; -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; /** * The Exit Command to shutdown Amy. @@ -34,11 +32,11 @@ */ public class ExitConsole { - @Reference - private Core core; - + /** + * Stops the program + */ @Command public void exit() { - this.core.stop(); + System.exit(0); // NOSONAR } } diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/io/CommandLineArgumentHandlerService.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/io/CommandLineArgumentHandlerService.java index 6f9aa044a..f3618a9cc 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/io/CommandLineArgumentHandlerService.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/io/CommandLineArgumentHandlerService.java @@ -163,6 +163,15 @@ private void processFlagParameter(FlagParameterInformation flagParaInfo, String } } + /** + * Checks whether the flags are valid. + * + * @return Whether the flags are valid. + */ + public boolean areFlagsValid() { + return this.flagsValid; + } + /** * @return Whether the program should continue execution. * @throws IllegalStateException diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/logger/LoggerProvider.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/logger/LoggerProvider.java index 6027dfb5e..1eda9895f 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/logger/LoggerProvider.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/logger/LoggerProvider.java @@ -30,17 +30,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import de.unistuttgart.iaas.amyassist.amy.core.di.Context; -import de.unistuttgart.iaas.amyassist.amy.core.di.ContextLocator; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.*; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandleImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceImplementationDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; /** * The Logger Provider for all Services @@ -55,21 +49,22 @@ public class LoggerProvider implements ServiceProvider { } @Override - public ServiceImplementationDescription getServiceImplementationDescription(@Nonnull ContextLocator locator, + public ServiceInstantiationDescription getServiceInstantiationDescription(@Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { - return new ServiceImplementationDescriptionImpl<>(serviceConsumer.getServiceDescription(), + return new ServiceInstantiationDescriptionImpl<>(serviceConsumer.getServiceDescription(), Collections.singletonMap(Context.CLASS, serviceConsumer.getConsumerClass()), LoggerFactory.class); } @Override - public @Nonnull ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { - Class cls = (Class) serviceImplementationDescription.getContext().get(Context.CLASS); - return new ServiceHandleImpl<>(LoggerFactory.getLogger(cls)); + public @Nonnull Logger createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + Class cls = (Class) serviceInstantiationDescription.getContext().get(Context.CLASS); + return LoggerFactory.getLogger(cls); } @Override - public void dispose(ServiceHandle service) { + public void dispose(@Nonnull Logger service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { // Logger MUST NOT be disposed } diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/persistence/storage/DatabaseStorage.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/persistence/storage/DatabaseStorage.java index abc1108cd..db7afbdc0 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/persistence/storage/DatabaseStorage.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/persistence/storage/DatabaseStorage.java @@ -78,8 +78,13 @@ public boolean has(String key) { @Override public void delete(String key) { - SimpleData find = this.entityManager.find(SimpleData.class, this.prefix + key); - this.entityManager.remove(find); + this.entityManager.getTransaction().begin(); + try { + SimpleData find = this.entityManager.find(SimpleData.class, this.prefix + key); + this.entityManager.remove(find); + } finally { + this.entityManager.getTransaction().commit(); + } } @PreDestroy diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/DeploymentContainerServiceExtension.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/DeploymentContainerServiceExtension.java index 19ffdde1e..526c0b1e3 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/DeploymentContainerServiceExtension.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/DeploymentContainerServiceExtension.java @@ -28,6 +28,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.DependencyInjection; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumerImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.extension.Extension; @@ -38,13 +39,13 @@ */ public class DeploymentContainerServiceExtension implements Extension { - private DependencyInjection di; + private ServiceLocator serviceLocator; private final Set> deploymentContainerServices = new HashSet<>(); @Override public void postConstruct(DependencyInjection dependencyInjection) { - this.di = dependencyInjection; + this.serviceLocator = dependencyInjection.getServiceLocator(); } @Override @@ -59,7 +60,7 @@ public void onRegister(ServiceDescription serviceDescription, Class deploymentContainerServiceDescription : this.deploymentContainerServices) { - DeploymentContainerService deploymentContainerService = (DeploymentContainerService) this.di + DeploymentContainerService deploymentContainerService = (DeploymentContainerService) this.serviceLocator .getService(new ServiceConsumerImpl<>(this.getClass(), deploymentContainerServiceDescription)) .getService(); deploymentContainerService.deploy(); diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/RunnableServiceExtension.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/RunnableServiceExtension.java index ff04656b7..08ee778d6 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/RunnableServiceExtension.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/service/RunnableServiceExtension.java @@ -28,6 +28,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.DependencyInjection; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.extension.Extension; /** @@ -37,13 +38,13 @@ */ public class RunnableServiceExtension implements Extension { - private DependencyInjection di; + private ServiceLocator serviceLocator; private final Set> runnableServices = new HashSet<>(); @Override public void postConstruct(DependencyInjection dependencyInjection) { - this.di = dependencyInjection; + this.serviceLocator = dependencyInjection.getServiceLocator(); } @Override @@ -57,7 +58,7 @@ public void onRegister(ServiceDescription serviceDescription, Class this.voiceOutput(response.getText())); this.sphinxGrammarCreator.createGrammar(SPHINX_MAIN_GARMMAR_NAME, this.keywordStartSingle, this.keywordStartMulti, this.keywordEndMulti, this.keywordStopOutput); } diff --git a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/TaskSchedulerImpl.java b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/TaskSchedulerImpl.java index e1b505316..54e04218b 100644 --- a/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/TaskSchedulerImpl.java +++ b/core/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/taskscheduler/TaskSchedulerImpl.java @@ -27,8 +27,11 @@ import java.time.temporal.ChronoUnit; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import javax.annotation.Nonnull; + import org.slf4j.Logger; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; @@ -58,30 +61,41 @@ private void setup() { this.scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "TaskScheduler")); } + private Runnable handleException(Runnable task) { + return () -> { + try { + task.run(); + } catch (RuntimeException e) { + this.logger.error("Exception while executing scheduled task.", e); + throw e; + } + }; + } + @Override public void execute(Runnable runnable) { - this.scheduledExecutorService.execute(runnable); + this.scheduledExecutorService.execute(this.handleException(runnable)); } @Override - public void schedule(Runnable task, Instant instant) { + public @Nonnull ScheduledFuture schedule(Runnable task, Instant instant) { this.logger.debug("schedule task for {}", instant); long delay = ChronoUnit.MILLIS.between(this.environment.getCurrentDateTime().toInstant(), instant); this.logger.debug("the delay of the task is {} ms", delay); - this.scheduledExecutorService.schedule(task, delay, TimeUnit.MILLISECONDS); + return this.scheduledExecutorService.schedule(this.handleException(task), delay, TimeUnit.MILLISECONDS); + } + + @Override + public @Nonnull ScheduledFuture schedule(Runnable task, long delay, TimeUnit timeUnit) { + this.logger.debug("schedule task with delay of {} {}", delay, timeUnit); + return this.scheduledExecutorService.schedule(this.handleException(task), delay, timeUnit); } - /** - * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#start() - */ @Override public void start() { // Do nothing. } - /** - * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#stop() - */ @Override public void stop() { this.scheduledExecutorService.shutdownNow(); diff --git a/core/src/main/resources/META-INF/core.config.properties b/core/src/main/resources/META-INF/core.config.properties index 6a27be405..a83d0d3d8 100644 --- a/core/src/main/resources/META-INF/core.config.properties +++ b/core/src/main/resources/META-INF/core.config.properties @@ -1,3 +1 @@ enableConsole=true -enableStemmer=true -chooseLanguage=en diff --git a/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/console/ConsoleTest.java b/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/console/ConsoleTest.java index 7840b7595..3827db8fc 100644 --- a/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/console/ConsoleTest.java +++ b/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/console/ConsoleTest.java @@ -53,9 +53,9 @@ void init() { ConfigurationManager configurationManager = Mockito.mock(ConfigurationManager.class); this.properties = new Properties(); Mockito.when(configurationManager.getConfigurationWithDefaults("core.config")).thenReturn(this.properties); - this.dependencyInjection + this.dependencyInjection.getConfiguration() .register(new SingletonServiceProvider<>(ConfigurationManager.class, configurationManager)); - this.dependencyInjection.register(new LoggerProvider()); + this.dependencyInjection.getConfiguration().register(new LoggerProvider()); } @Test @@ -68,8 +68,9 @@ void test() { Mockito.when(handler.createDialog(ArgumentMatchers.any())).thenReturn(uuid); - this.dependencyInjection.register(new SingletonServiceProvider<>(DialogHandler.class, handler)); - SpeechConsole console = this.dependencyInjection.createAndInitialize(SpeechConsole.class); + this.dependencyInjection.getConfiguration() + .register(new SingletonServiceProvider<>(DialogHandler.class, handler)); + SpeechConsole console = this.dependencyInjection.getServiceLocator().createAndInitialize(SpeechConsole.class); console.say(testInput); Mockito.verify(handler).process(expected, uuid); diff --git a/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/service/ServiceManagerImplTest.java b/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/service/ServiceManagerImplTest.java index 083d308cf..626edcb15 100644 --- a/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/service/ServiceManagerImplTest.java +++ b/core/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/service/ServiceManagerImplTest.java @@ -45,7 +45,7 @@ class ServiceManagerImplTest { public void setup() { this.runnableServiceExtension = new RunnableServiceExtension(); this.di = new DependencyInjection(this.runnableServiceExtension); - this.di.register(ServiceManagerImpl.class); + this.di.getConfiguration().register(ServiceManagerImpl.class); } /** @@ -53,12 +53,11 @@ public void setup() { */ @Test void testStart() { - - this.di.register(TestRunnableService.class); + this.di.getConfiguration().register(TestRunnableService.class); this.runnableServiceExtension.deploy(); this.runnableServiceExtension.start(); - TestRunnableService service = this.di.getService(TestRunnableService.class); + TestRunnableService service = this.di.getServiceLocator().getService(TestRunnableService.class); assertThat(service.run, is(true)); this.runnableServiceExtension.stop(); assertThat(service.run, is(false)); @@ -80,12 +79,11 @@ void testStartDouble() { */ @Test void testStartRunnableServiceFromInterface() { - - this.di.register(SimpleServiceImpl.class); + this.di.getConfiguration().register(SimpleServiceImpl.class); this.runnableServiceExtension.deploy(); this.runnableServiceExtension.start(); - SimpleService service = this.di.getService(SimpleService.class); + SimpleService service = this.di.getServiceLocator().getService(SimpleService.class); assertThat(service.getRun(), is(true)); this.runnableServiceExtension.stop(); assertThat(service.getRun(), is(false)); diff --git a/deployment-descriptor-utility/pom.xml b/deployment-descriptor-utility/pom.xml index 40433bb8f..ea9562acf 100644 --- a/deployment-descriptor-utility/pom.xml +++ b/deployment-descriptor-utility/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/di-api/pom.xml b/di-api/pom.xml index 0a651f98c..7656c32ce 100644 --- a/di-api/pom.xml +++ b/di-api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceImplementationDescription.java b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceInstantiationDescription.java similarity index 75% rename from di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceImplementationDescription.java rename to di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceInstantiationDescription.java index dc3e9a49c..c523a5ec2 100644 --- a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceImplementationDescription.java +++ b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceInstantiationDescription.java @@ -27,16 +27,20 @@ import javax.annotation.Nonnull; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; + /** - * Information about the implementation of a Service and the context in which the service is provided. This Descriptor - * is created from The ServiceProvider which should provide the Service. The Informations is needed by the Dependency - * Injection to manage the Service instance. + * Information needed to instantiate the Service. This includes information about the implementation of a Service and + * the context in which the service is provided. This Descriptor is created from the + * {@link ServiceProvider#getServiceInstantiationDescription(ContextLocator, ServiceConsumer)} which should provide the + * Service. The Informations is needed by the Dependency Injection to manage the Service instance. * * @author Leon Kiefer * @param * the type of the service */ -public interface ServiceImplementationDescription { +public interface ServiceInstantiationDescription { /** * The Service description independent of this implementation specific description. It is used to find a matching * ServiceProvider. @@ -65,5 +69,5 @@ public interface ServiceImplementationDescription { * that implements the contract types */ @Nonnull - public Class getImplementationClass(); + Class getImplementationClass(); } diff --git a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocator.java b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocator.java index 5215ae74d..1b013431a 100644 --- a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocator.java +++ b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocator.java @@ -23,19 +23,21 @@ package de.unistuttgart.iaas.amyassist.amy.core.di; +import javax.annotation.Nonnull; + import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; /** - * SimpleServiceLocator is used in {@link ServiceProvider} to get dependencies. The normal extended {@link ServiceLocator} interface - * should not be used in ServiceProviders. + * SimpleServiceLocator is used in {@link ServiceProvider} to get dependencies. The normal extended + * {@link ServiceLocator} interface should not be used in ServiceProviders. * * @author Leon Kiefer */ public interface SimpleServiceLocator { /** - * Get a service for the given service consumer. This Method track the caller to prevent the service get created + * Get a service for the given service consumer. This method track the caller to prevent the service get created * twice and to run in a dependency circle. * * @param serviceConsumer @@ -44,6 +46,6 @@ public interface SimpleServiceLocator { * @param * the type of the service */ - ServiceHandle getService(ServiceConsumer serviceConsumer); + ServiceHandle getService(@Nonnull ServiceConsumer serviceConsumer); } diff --git a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceProvider.java b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceProvider.java index 8a57faf43..460db426d 100644 --- a/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceProvider.java +++ b/di-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceProvider.java @@ -28,7 +28,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.ContextLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceInstantiationDescription; import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; @@ -55,43 +55,45 @@ public interface ServiceProvider { /** * Refine the ServiceDescription of a ServiceConsumer. The ServiceProvider can decide if it can provide the * requested service. If this ServiceProvider can provide the service, it returns the - * ServiceImplementationDescription for the Service. The ServiceImplementationDescription contains all information - * needed to create the requested Service for the Consumer. + * ServiceInstantiationDescription for the Service. The ServiceInstantiationDescription contains all information + * needed to create the requested Service for the ServiceConsumer. * * @param locator * the ContextLocator which can be used to lookup ContextProvider * @param serviceConsumer * the consumer of the Service. This can be used to extract context information. - * @return the ServiceImplementationDescription for the Service this Provider can provide or null if this provider + * @return the ServiceInstantiationDescription for the Service this Provider can provide or null if this provider * can not provide the requested Service. */ @CheckForNull - ServiceImplementationDescription getServiceImplementationDescription(@Nonnull ContextLocator locator, + ServiceInstantiationDescription getServiceInstantiationDescription(@Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer); /** - * Create a new Service from the given ServiceImplementationDescription. Using the ServiceLocator to lookup + * Create a new Service from the given ServiceInstantiationDescription. Using the ServiceLocator to lookup * dependencies. * * @param locator - * the ServiceLocator to lookup services - * @param serviceImplementationDescription + * the ServiceLocator to lookup services, don't pass this reference to any created Service + * @param serviceInstantiationDescription * the description of the Service which must be created * - * @return the created service of this ServiceProvider for the given ServiceImplementationDescription + * @return the created service of this ServiceProvider for the given ServiceInstantiationDescription */ @Nonnull - ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription); + T createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription); /** * Dispose a Service that was provided by this ServiceProvider. * * @param service * the Service that should be disposed + * @param serviceInstantiationDescription + * the description of the Service which should be disposed * @throws IllegalArgumentException * if the given Service was not provided by this ServiceProvider. */ - void dispose(ServiceHandle service); + void dispose(@Nonnull T service, @Nonnull ServiceInstantiationDescription serviceInstantiationDescription); } diff --git a/di/pom.xml b/di/pom.xml index e6fdf3efd..6364327fb 100644 --- a/di/pom.xml +++ b/di/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjection.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjection.java index 4e9aacb6d..c87d72286 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjection.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjection.java @@ -23,36 +23,17 @@ package de.unistuttgart.iaas.amyassist.amy.core.di; -import java.lang.reflect.Field; import java.util.Arrays; import java.util.HashSet; -import java.util.Map; import java.util.ServiceLoader; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import javax.annotation.CheckForNull; -import javax.annotation.Nonnull; import javax.annotation.ParametersAreNullableByDefault; -import org.apache.commons.lang3.reflect.FieldUtils; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; -import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ConsumerFactory; -import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumerImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.context.provider.ClassProvider; -import de.unistuttgart.iaas.amyassist.amy.core.di.context.provider.StaticProvider; import de.unistuttgart.iaas.amyassist.amy.core.di.extension.Extension; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ClassServiceProvider; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.SingletonServiceProvider; -import de.unistuttgart.iaas.amyassist.amy.core.di.util.Util; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; /** * Dependency Injection Used to manage dependencies and Service instantiation at runtime. A Service that relies on DI is @@ -65,21 +46,11 @@ * @author Leon Kiefer, Tim Neumann */ @ParametersAreNullableByDefault -public class DependencyInjection implements ServiceLocator, Configuration { - /** - * A register which maps a service description to it's service provider. - */ - private final Map, ServiceProvider> register; - - private final Map, ServiceHandle> servicePool; - - private final Map, ServiceCreation> serviceCreationInfos; - - @Nonnull - private final ContextLocatorImpl contextLocator; - +public class DependencyInjection { private final Set extensions; + private final InternalServiceLocator internalServiceLocator; + /** * Creates a new Dependency Injection * @@ -87,18 +58,17 @@ public class DependencyInjection implements ServiceLocator, Configuration { * for the dependency injection */ public DependencyInjection(Extension... extensions) { - this.register = new ConcurrentHashMap<>(); - this.servicePool = new ConcurrentHashMap<>(); - this.serviceCreationInfos = new ConcurrentHashMap<>(); - this.contextLocator = new ContextLocatorImpl(); - this.extensions = new HashSet<>(Arrays.asList(extensions)); + this.internalServiceLocator = new InternalServiceLocator(this::onRegister); - this.registerContextProvider("class", new ClassProvider()); - this.register(new SingletonServiceProvider<>(ServiceLocator.class, this)); - this.register(new SingletonServiceProvider<>(Configuration.class, this)); + this.extensions = new HashSet<>(Arrays.asList(extensions)); this.extensions.forEach(ext -> ext.postConstruct(this)); } + private void onRegister(ClassServiceProvider classServiceProvider) { + this.extensions.forEach(ext -> ext.onRegister(classServiceProvider.getServiceDescription(), + classServiceProvider.getImplementationClass())); + } + /** * Loads Services using the provider configuration file * META-INF/services/de.unistuttgart.iaas.amyassist.amy.core.di.ServiceProviderLoader and the @@ -107,227 +77,29 @@ public DependencyInjection(Extension... extensions) { * @see java.util.ServiceLoader */ public void loadServices() { - ServiceLoader.load(ServiceProviderLoader.class).forEach(s -> s.load(this)); - } - - @Override - public void register(@Nonnull ServiceProvider serviceProvider) { - ServiceDescription serviceDescription = serviceProvider.getServiceDescription(); - ServiceKey serviceKey = new ServiceKey<>(serviceDescription); - synchronized (this.register) { - if (this.register.containsKey(serviceKey)) - throw new DuplicateServiceException(serviceDescription); - this.register.put(serviceKey, serviceProvider); - } - } - - @Override - public void register(@Nonnull Class cls) { - Service annotation = cls.getAnnotation(Service.class); - if (annotation == null) - throw new ClassIsNotAServiceException(cls); - @Nonnull - Class serviceType = annotation.value(); - if (serviceType.equals(Void.class)) { - Class[] interfaces = cls.getInterfaces(); - if (interfaces.length == 1) { - // annotations can not have null values - serviceType = interfaces[0]; - } else if (interfaces.length == 0) { - serviceType = cls; - } else { - throw new IllegalArgumentException("The type of the service implementation " + cls.getName() - + " is ambiguous, because the type is not given by the annotation and multiple interfaces are implemented." - + " Please specify which type this service should have."); - } - } - this.registerClass(cls, serviceType); - } - - private void registerClass(@Nonnull Class cls, @Nonnull Class serviceType) { - if (!serviceType.isAssignableFrom(cls)) { - throw new IllegalArgumentException( - "The specified service type " + serviceType.getName() + " is not assignable from " + cls.getName()); - } - Class implementationClass = (Class) cls; - ClassServiceProvider classServiceProvider = new ClassServiceProvider<>(serviceType, implementationClass); - this.register(classServiceProvider); - this.extensions - .forEach(ext -> ext.onRegister(classServiceProvider.getServiceDescription(), implementationClass)); - } - - @Override - public void registerContextProvider(String key, StaticProvider staticProvider) { - this.contextLocator.registerContextProvider(key, staticProvider); - } - - @Override - public T getService(Class serviceType) { - return this.getService(new ServiceDescriptionImpl<>(serviceType)).getService(); - } - - @Override - public ServiceHandle getService(ServiceDescription serviceDescription) { - return this.getService(new ServiceConsumerImpl<>(this.getClass(), serviceDescription)); - } - - @Override - public ServiceHandle getService(@Nonnull ServiceConsumer serviceConsumer) { - return this.getService(new ServiceCreation<>(serviceConsumer.getConsumerClass().getName()), serviceConsumer); + ServiceLoader.load(ServiceProviderLoader.class).forEach(s -> s.load(this.getConfiguration())); } /** - * Get the service with the tracking of the dependency hierarchy. - * - * @param dependentServiceCreation - * @param serviceConsumer - * @param - * the type of the service - * @return the service handle of the service + * @return the instance of the Configuration owned by the DependencyInjection */ - ServiceHandle getService(@Nonnull ServiceCreation dependentServiceCreation, - @Nonnull ServiceConsumer serviceConsumer) { - ServiceProvider provider = this.getServiceProvider(serviceConsumer.getServiceDescription(), - dependentServiceCreation); - ServiceImplementationDescription serviceImplementationDescription = provider - .getServiceImplementationDescription(this.contextLocator, serviceConsumer); - if (serviceImplementationDescription == null) { - throw new ServiceNotFoundException(serviceConsumer.getServiceDescription(), dependentServiceCreation); - } - return this.lookUpOrCreateService(dependentServiceCreation, provider, serviceImplementationDescription); - } - - @SuppressWarnings("unchecked") - @CheckForNull - private ServiceHandle lookUpService(@Nonnull ServiceProvider serviceProvider, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { - - ServicePoolKey servicePoolKey = new ServicePoolKey<>(serviceProvider, serviceImplementationDescription); - - if (!this.servicePool.containsKey(servicePoolKey)) { - return null; - } - return (ServiceHandle) this.servicePool.get(servicePoolKey); - } - - @SuppressWarnings("unchecked") - private Future> createService(@Nonnull ServiceCreation dependentServiceCreation, - @Nonnull ServiceProvider serviceProvider, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { - ServicePoolKey key = new ServicePoolKey<>(serviceProvider, serviceImplementationDescription); - synchronized (this.servicePool) { - ServiceCreation serviceCreation; - if (this.serviceCreationInfos.containsKey(key)) { - serviceCreation = (ServiceCreation) this.serviceCreationInfos.get(key); - } else { - serviceCreation = new ServiceCreation<>( - serviceImplementationDescription.getImplementationClass().getName()); - - serviceCreation.completableFuture = CompletableFuture.supplyAsync(() -> { - ServiceHandle service = serviceProvider.createService( - new SimpleServiceLocatorImpl(this, serviceCreation), serviceImplementationDescription); - this.servicePool.put(key, service); - return service; - }); - - this.serviceCreationInfos.put(key, serviceCreation); - } - serviceCreation.addDependent(dependentServiceCreation); - - return serviceCreation.completableFuture; - } - } - - private ServiceHandle lookUpOrCreateService(@Nonnull ServiceCreation dependentServiceCreationInfo, - @Nonnull ServiceProvider serviceProvider, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { - ServiceHandle lookUpService = this.lookUpService(serviceProvider, serviceImplementationDescription); - if (lookUpService == null) { - Future> createService = this.createService(dependentServiceCreationInfo, serviceProvider, - serviceImplementationDescription); - try { - lookUpService = createService.get(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - throw new IllegalStateException(e); - } catch (ExecutionException e) { - if (e.getCause() instanceof RuntimeException) { - throw (RuntimeException) e.getCause(); - } - throw new IllegalStateException("Checked exception thrown", e); - } - } - return lookUpService; - } - - @Override - public void inject(@Nonnull Object instance) { - Class classOfInstance = instance.getClass(); - Field[] dependencyFields = FieldUtils.getFieldsWithAnnotation(classOfInstance, Reference.class); - for (Field field : dependencyFields) { - ServiceDescription serviceDescription = Util.serviceDescriptionFor(field); - serviceDescription.getAnnotations().removeIf(annotation -> annotation instanceof Reference); - Class declaredClass = field.getDeclaringClass(); - Object object = this.getService(ConsumerFactory.build(declaredClass, serviceDescription)).getService(); - Util.inject(instance, object, field); - } - } - - @Override - public void postConstruct(@Nonnull Object postConstructMe) { - Util.postConstruct(postConstructMe); + public Configuration getConfiguration() { + return this.internalServiceLocator + .getService( + new ServiceConsumerImpl<>(this.getClass(), new ServiceDescriptionImpl<>(Configuration.class))) + .getService(); } /** - * This is the only method that gets ServiceProvider from the register and casts them to the correct return type + * The ServiceLocator of the DependencyInjection * - * @param - * the type of the service - * @param serviceDescription - * the description of the wanted service - * @return the ServiceProvider that can provide matching services for the given ServiceDescription - * @throws ServiceNotFoundException - * if no service is found for the given ServiceDescription + * @return the instance of the ServiceLocator owned by the DependencyInjection */ - @Nonnull - @SuppressWarnings("unchecked") - private ServiceProvider getServiceProvider(ServiceDescription serviceDescription, - ServiceCreation serviceCreation) { - ServiceKey serviceKey = new ServiceKey<>(serviceDescription); - synchronized (this.register) { - if (!this.register.containsKey(serviceKey)) - throw new ServiceNotFoundException(serviceDescription, serviceCreation); - return (ServiceProvider) this.register.get(serviceKey); - } - } - - @Override - public T createAndInitialize(@Nonnull Class serviceClass) { - if (!Util.isValidServiceClass(serviceClass)) { - throw new IllegalArgumentException( - "There is a problem with the class " + serviceClass.getName() + ". It can't be used as a Service"); - } - T newInstance; - try { - newInstance = serviceClass.newInstance(); - } catch (InstantiationException | IllegalAccessException e) { - throw new IllegalStateException( - "The constructor of " + serviceClass.getName() + " should have been checked", e); - } - this.inject(newInstance); - this.postConstruct(newInstance); - - return newInstance; - } - - @Override - public void preDestroy(@Nonnull Object destroyMe) { - Util.preDestroy(destroyMe); - } - - @Override - public void shutdown() { - // TODO manage the lifecycle + public ServiceLocator getServiceLocator() { + return this.internalServiceLocator + .getService( + new ServiceConsumerImpl<>(this.getClass(), new ServiceDescriptionImpl<>(ServiceLocator.class))) + .getService(); } } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/InternalServiceLocator.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/InternalServiceLocator.java new file mode 100644 index 000000000..316dda523 --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/InternalServiceLocator.java @@ -0,0 +1,213 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di; + +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.function.Consumer; + +import javax.annotation.CheckForNull; +import javax.annotation.Nonnull; +import javax.annotation.ParametersAreNullableByDefault; + +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.context.provider.ClassProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.context.provider.StaticProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.DuplicateServiceException; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.ServiceNotFoundException; +import de.unistuttgart.iaas.amyassist.amy.core.di.provider.*; + +/** + * This is the core of the dependency injection. It contains the maps with ServiceProviders and instantiated Services. + * It is responsible for managing the services and creating new Services by need. All Operations are Thread safe and be + * mostly processed concurrently. + * + * @author Leon Kiefer + */ +@ParametersAreNullableByDefault +public class InternalServiceLocator implements SimpleServiceLocator { + /** + * A register which maps a service description to it's service provider. + */ + private final Map, ServiceProvider> register; + + private final Map, ServiceHandle> servicePool; + + private final Map, ServiceCreation> serviceCreationInfos; + + @Nonnull + private final ContextLocatorImpl contextLocator; + + private Consumer> onRegister; + + /** + * + * @param onRegister + */ + public InternalServiceLocator(Consumer> onRegister) { + this.onRegister = onRegister; + this.register = new ConcurrentHashMap<>(); + this.servicePool = new ConcurrentHashMap<>(); + this.serviceCreationInfos = new ConcurrentHashMap<>(); + this.contextLocator = new ContextLocatorImpl(); + + this.registerContextProvider("class", new ClassProvider()); + this.register(new ServiceLocatorProvider(this)); + this.register(new ConfigurationProvider(this)); + } + + public void register(@Nonnull ServiceProvider serviceProvider) { + ServiceDescription serviceDescription = serviceProvider.getServiceDescription(); + ServiceKey serviceKey = new ServiceKey<>(serviceDescription); + synchronized (this.register) { + if (this.register.containsKey(serviceKey)) + throw new DuplicateServiceException(serviceDescription); + this.register.put(serviceKey, serviceProvider); + } + } + + public void onRegister(ClassServiceProvider classServiceProvider) { + this.onRegister.accept(classServiceProvider); + } + + public void registerContextProvider(String key, StaticProvider staticProvider) { + this.contextLocator.registerContextProvider(key, staticProvider); + } + + @Override + public ServiceHandle getService(@Nonnull ServiceConsumer serviceConsumer) { + return this.getService(new ServiceCreation<>(serviceConsumer.getConsumerClass().getName()), serviceConsumer); + } + + /** + * Get the service with the tracking of the dependency hierarchy. + * + * @param dependentServiceCreation + * @param serviceConsumer + * @param + * the type of the service + * @return the service handle of the service + */ + ServiceHandle getService(@Nonnull ServiceCreation dependentServiceCreation, + @Nonnull ServiceConsumer serviceConsumer) { + ServiceProvider provider = this.getServiceProvider(serviceConsumer.getServiceDescription(), + dependentServiceCreation); + ServiceInstantiationDescription serviceInstantiationDescription = provider + .getServiceInstantiationDescription(this.contextLocator, serviceConsumer); + if (serviceInstantiationDescription == null) { + throw new ServiceNotFoundException(serviceConsumer.getServiceDescription(), dependentServiceCreation); + } + return this.lookUpOrCreateService(dependentServiceCreation, provider, serviceInstantiationDescription); + } + + @SuppressWarnings("unchecked") + @CheckForNull + private ServiceHandle lookUpService(@Nonnull ServiceProvider serviceProvider, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + ServicePoolKey servicePoolKey = new ServicePoolKey<>(serviceProvider, serviceInstantiationDescription); + + if (!this.servicePool.containsKey(servicePoolKey)) { + return null; + } + return (ServiceHandle) this.servicePool.get(servicePoolKey); + } + + @SuppressWarnings("unchecked") + private Future> createService(@Nonnull ServiceCreation dependentServiceCreation, + @Nonnull ServiceProvider serviceProvider, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + ServicePoolKey key = new ServicePoolKey<>(serviceProvider, serviceInstantiationDescription); + synchronized (this.servicePool) { + ServiceCreation serviceCreation; + if (this.serviceCreationInfos.containsKey(key)) { + serviceCreation = (ServiceCreation) this.serviceCreationInfos.get(key); + } else { + serviceCreation = new ServiceCreation<>( + serviceInstantiationDescription.getImplementationClass().getName()); + + serviceCreation.completableFuture = CompletableFuture.supplyAsync(() -> { + SimpleServiceLocatorImpl tempLocator = new SimpleServiceLocatorImpl(this, serviceCreation); + T service = serviceProvider.createService(tempLocator, serviceInstantiationDescription); + tempLocator.destroy(); + ServiceHandle serviceHandle = new ServiceHandleImpl<>(service); + this.servicePool.put(key, serviceHandle); + return serviceHandle; + }); + + this.serviceCreationInfos.put(key, serviceCreation); + } + serviceCreation.addDependent(dependentServiceCreation); + + return serviceCreation.completableFuture; + } + } + + private ServiceHandle lookUpOrCreateService(@Nonnull ServiceCreation dependentServiceCreationInfo, + @Nonnull ServiceProvider serviceProvider, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + ServiceHandle lookUpService = this.lookUpService(serviceProvider, serviceInstantiationDescription); + if (lookUpService == null) { + Future> createService = this.createService(dependentServiceCreationInfo, serviceProvider, + serviceInstantiationDescription); + try { + lookUpService = createService.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IllegalStateException(e); + } catch (ExecutionException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new IllegalStateException("Checked exception thrown", e); + } + } + return lookUpService; + } + + /** + * This is the only method that gets ServiceProvider from the register and casts them to the correct return type + * + * @param + * the type of the service + * @param serviceDescription + * the description of the wanted service + * @return the ServiceProvider that can provide matching services for the given ServiceDescription + * @throws ServiceNotFoundException + * if no service is found for the given ServiceDescription + */ + @Nonnull + @SuppressWarnings("unchecked") + private ServiceProvider getServiceProvider(ServiceDescription serviceDescription, + ServiceCreation serviceCreation) { + ServiceKey serviceKey = new ServiceKey<>(serviceDescription); + synchronized (this.register) { + if (!this.register.containsKey(serviceKey)) + throw new ServiceNotFoundException(serviceDescription, serviceCreation); + return (ServiceProvider) this.register.get(serviceKey); + } + } +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceCreation.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceCreation.java index ea551c76d..77cb7d57b 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceCreation.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceCreation.java @@ -38,7 +38,7 @@ * @param * the type of the created service */ -class ServiceCreation { +public class ServiceCreation { CompletableFuture> completableFuture; private final Set> dependents = new HashSet<>(); private final String name; @@ -48,7 +48,7 @@ class ServiceCreation { * the name for the debugging * */ - public ServiceCreation(String name) { + ServiceCreation(String name) { this.name = name; } @@ -91,6 +91,11 @@ public String toString() { return this.print(); } + /** + * Create a tree of the dependents. + * + * @return the tree as string to print to the console + */ public String print() { return this.print("\n"); } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceHandleImpl.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceHandleImpl.java similarity index 87% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceHandleImpl.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceHandleImpl.java index fc017566f..702599e63 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceHandleImpl.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceHandleImpl.java @@ -21,16 +21,18 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di.provider; +package de.unistuttgart.iaas.amyassist.amy.core.di; import javax.annotation.Nonnull; +import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; + /** * The implementation of the ServiceHandle * * @author Leon Kiefer */ -public class ServiceHandleImpl implements ServiceHandle { +class ServiceHandleImpl implements ServiceHandle { @Nonnull private final T service; diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServicePoolKey.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServicePoolKey.java index 6b0b56b0c..f3b9086d9 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServicePoolKey.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServicePoolKey.java @@ -70,13 +70,13 @@ public ServicePoolKey(@Nonnull ServiceProvider serviceProvider, Map serviceProvider, - ServiceImplementationDescription serviceImplementationDescription) { - this(serviceProvider, serviceImplementationDescription.getContext(), - serviceImplementationDescription.getServiceDescription().getServiceType(), - serviceImplementationDescription.getImplementationClass()); + ServiceInstantiationDescription serviceInstantiationDescription) { + this(serviceProvider, serviceInstantiationDescription.getContext(), + serviceInstantiationDescription.getServiceDescription().getServiceType(), + serviceInstantiationDescription.getImplementationClass()); } @Override @@ -96,7 +96,7 @@ public boolean equals(Object obj) { return false; if (this.getClass() != obj.getClass()) return false; - ServicePoolKey other = (ServicePoolKey) obj; + ServicePoolKey other = (ServicePoolKey) obj; if (!this.serviceProvider.equals(other.serviceProvider)) return false; if (!this.context.equals(other.context)) @@ -106,8 +106,8 @@ public boolean equals(Object obj) { @Override public String toString() { - return "Service type: " + this.serviceType.getName() + " ServiceProvider: " + this.serviceProvider - + " Implementation class: " + this.implementationClass.getName() + " Context: " + this.context; + return "Service type: " + this.serviceType.getName() + "\nServiceProvider: " + this.serviceProvider + + "\nImplementation class: " + this.implementationClass.getName() + "\nContext: " + this.context; } } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocatorImpl.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocatorImpl.java index 2fef65fa8..ccebfce15 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocatorImpl.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/SimpleServiceLocatorImpl.java @@ -23,6 +23,8 @@ package de.unistuttgart.iaas.amyassist.amy.core.di; +import javax.annotation.Nonnull; + import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; @@ -31,22 +33,31 @@ * * @author Leon Kiefer */ -public class SimpleServiceLocatorImpl implements SimpleServiceLocator { +class SimpleServiceLocatorImpl implements SimpleServiceLocator { - private DependencyInjection dependencyInjection; + private InternalServiceLocator internalServiceLocator; private ServiceCreation serviceCreationInfo; /** - * @param dependencyInjection + * @param internalServiceLocator * @param serviceCreationInfo */ - public SimpleServiceLocatorImpl(DependencyInjection dependencyInjection, ServiceCreation serviceCreationInfo) { - this.dependencyInjection = dependencyInjection; + public SimpleServiceLocatorImpl(InternalServiceLocator internalServiceLocator, + ServiceCreation serviceCreationInfo) { + this.internalServiceLocator = internalServiceLocator; this.serviceCreationInfo = serviceCreationInfo; } @Override - public ServiceHandle getService(ServiceConsumer serviceConsumer) { - return this.dependencyInjection.getService(this.serviceCreationInfo, serviceConsumer); + public ServiceHandle getService(@Nonnull ServiceConsumer serviceConsumer) { + return this.internalServiceLocator.getService(this.serviceCreationInfo, serviceConsumer); + } + + /** + * destroy the references + */ + public void destroy() { + this.internalServiceLocator = null; + this.serviceCreationInfo = null; } } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ClassIsNotAServiceException.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ClassIsNotAServiceException.java similarity index 95% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ClassIsNotAServiceException.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ClassIsNotAServiceException.java index dfcaffe84..e9328e6a2 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ClassIsNotAServiceException.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ClassIsNotAServiceException.java @@ -21,7 +21,7 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di; +package de.unistuttgart.iaas.amyassist.amy.core.di.exception; /** * A exception of the dependency injection, signaling, that a given class is not a service. diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DuplicateServiceException.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/DuplicateServiceException.java similarity index 91% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DuplicateServiceException.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/DuplicateServiceException.java index 3462aebe8..63e331dc1 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/DuplicateServiceException.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/DuplicateServiceException.java @@ -21,7 +21,9 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di; +package de.unistuttgart.iaas.amyassist.amy.core.di.exception; + +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; /** * A exception of the dependency injection, signaling, that a service is already registered diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceNotFoundException.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ServiceNotFoundException.java similarity index 92% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceNotFoundException.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ServiceNotFoundException.java index 3b390b9e6..6c9347061 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceNotFoundException.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/exception/ServiceNotFoundException.java @@ -21,7 +21,10 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di; +package de.unistuttgart.iaas.amyassist.amy.core.di.exception; + +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceCreation; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; /** * A exception of the dependency injection diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ClassServiceProvider.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ClassServiceProvider.java index c3f4f96d9..6c12c278c 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ClassServiceProvider.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ClassServiceProvider.java @@ -35,12 +35,13 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.ContextLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceInstantiationDescription; import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Context; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.util.Util; /** @@ -70,10 +71,22 @@ public ClassServiceProvider(@Nonnull Class serviceType, @Nonnull Class(serviceType), cls); } + /** + * The class which is the implementation of the Service. + * + * @return the class + */ + @Nonnull + public Class getImplementationClass() { + return this.cls; + } + /** * * @param serviceDescription + * the description of the service that should be provided * @param cls + * the service implementation class */ public ClassServiceProvider(@Nonnull ServiceDescription serviceDescription, @Nonnull Class cls) { this.serviceDescription = serviceDescription; @@ -99,7 +112,7 @@ public ClassServiceProvider(@Nonnull ServiceDescription serviceDescription, @ } @Override - public ServiceImplementationDescription getServiceImplementationDescription(@Nonnull ContextLocator locator, + public ServiceInstantiationDescription getServiceInstantiationDescription(@Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { HashMap map = new HashMap<>(); for (ContextInjectionPoint c : this.contextInjectionPoints) { @@ -107,27 +120,28 @@ public ServiceImplementationDescription getServiceImplementationDescription(@ map.put(key, locator.getContextProvider(key).getContext(serviceConsumer)); } - return new ServiceImplementationDescriptionImpl<>(serviceConsumer.getServiceDescription(), map, this.cls); + return new ServiceInstantiationDescriptionImpl<>(serviceConsumer.getServiceDescription(), map, this.cls); } @Override - public @Nonnull ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { + public @Nonnull T createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { - @Nonnull T serviceInstance = this.createService(); + @Nonnull + T serviceInstance = this.createService(); for (InjectionPoint injectionPoint : this.injectionPoints) { ServiceConsumer serviceConsumer = injectionPoint.getServiceConsumer(); ServiceHandle serviceHandle = locator.getService(serviceConsumer); injectionPoint.inject(serviceInstance, serviceHandle.getService()); } - Map context = serviceImplementationDescription.getContext(); + Map context = serviceInstantiationDescription.getContext(); for (ContextInjectionPoint contextInjectionPoint : this.contextInjectionPoints) { contextInjectionPoint.inject(serviceInstance, context.get(contextInjectionPoint.getContextIdentifier())); } Util.postConstruct(serviceInstance); - return new ServiceHandleImpl<>(serviceInstance); + return serviceInstance; } private T createService() { @@ -140,8 +154,9 @@ private T createService() { } @Override - public void dispose(ServiceHandle service) { - Util.preDestroy(service.getService()); + public void dispose(@Nonnull T service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + Util.preDestroy(service); } } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationImpl.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationImpl.java new file mode 100644 index 000000000..74e05c7ab --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationImpl.java @@ -0,0 +1,67 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.provider; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.Configuration; +import de.unistuttgart.iaas.amyassist.amy.core.di.InternalServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.context.provider.StaticProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.util.ConfigurationUtil; + +/** + * Implementation of Configuration using the InternalServiceLocator. + * + * @author Leon Kiefer + */ +public class ConfigurationImpl implements Configuration { + + private InternalServiceLocator internalServiceLocator; + + /** + * @param internalServiceLocator + */ + public ConfigurationImpl(InternalServiceLocator internalServiceLocator) { + this.internalServiceLocator = internalServiceLocator; + } + + @Override + public void registerContextProvider(String key, StaticProvider staticProvider) { + this.internalServiceLocator.registerContextProvider(key, staticProvider); + } + + @Override + public void register(@Nonnull Class cls) { + ClassServiceProvider classServiceProvider = ConfigurationUtil.getClassServiceProvider(cls); + + this.register(classServiceProvider); + this.internalServiceLocator.onRegister(classServiceProvider); + } + + @Override + public void register(@Nonnull ServiceProvider serviceProvider) { + this.internalServiceLocator.register(serviceProvider); + } + +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationProvider.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationProvider.java new file mode 100644 index 000000000..e7fe1ec44 --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ConfigurationProvider.java @@ -0,0 +1,74 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.provider; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.*; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; + +/** + * Creates the ConfigurationImpl service. + * + * @author Leon Kiefer + */ +public class ConfigurationProvider implements ServiceProvider { + + private final InternalServiceLocator internalServiceLocator; + + /** + * @param internalServiceLocator + */ + public ConfigurationProvider(InternalServiceLocator internalServiceLocator) { + this.internalServiceLocator = internalServiceLocator; + } + + @Override + @Nonnull + public ServiceDescription getServiceDescription() { + return new ServiceDescriptionImpl<>(Configuration.class); + } + + @Override + public ServiceInstantiationDescription getServiceInstantiationDescription( + @Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { + return new ServiceInstantiationDescriptionImpl<>(this.getServiceDescription(), Configuration.class); + } + + @Override + @Nonnull + public Configuration createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + return new ConfigurationImpl(this.internalServiceLocator); + } + + @Override + public void dispose(@Nonnull Configuration service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + // nothing to do here + } + +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/InjectableServiceLocator.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/InjectableServiceLocator.java new file mode 100644 index 000000000..5f12f6351 --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/InjectableServiceLocator.java @@ -0,0 +1,99 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.provider; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumerImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.util.ServiceLocatorUtil; +import de.unistuttgart.iaas.amyassist.amy.core.di.util.Util; + +/** + * Facade for the {@link SimpleServiceLocator} + * + * @author Leon Kiefer + */ +class InjectableServiceLocator implements ServiceLocator { + @Nonnull + private final SimpleServiceLocator locator; + @Nonnull + private final Class consumerClass; + + /** + * @param locator + * the simple service locator used to resolve Services. + * @param consumerClass + * the class this instance is injected in + */ + public InjectableServiceLocator(@Nonnull SimpleServiceLocator locator, @Nonnull Class consumerClass) { + this.locator = locator; + this.consumerClass = consumerClass; + } + + @Override + public T createAndInitialize(@Nonnull Class serviceClass) { + return ServiceLocatorUtil.createAndInitialize(serviceClass, this.locator); + } + + @Override + public T getService(Class serviceType) { + return this.getService(new ServiceDescriptionImpl<>(serviceType)).getService(); + } + + @Override + public ServiceHandle getService(ServiceDescription serviceDescription) { + return this.getService(new ServiceConsumerImpl<>(this.consumerClass, serviceDescription)); + } + + @Override + public ServiceHandle getService(@Nonnull ServiceConsumer serviceConsumer) { + return this.locator.getService(serviceConsumer); + } + + @Override + public void inject(@Nonnull Object injectMe) { + ServiceLocatorUtil.inject(injectMe, this.locator); + } + + @Override + public void postConstruct(@Nonnull Object postConstructMe) { + Util.postConstruct(postConstructMe); + } + + @Override + public void preDestroy(@Nonnull Object destroyMe) { + Util.preDestroy(destroyMe); + } + + @Override + public void shutdown() { + throw new UnsupportedOperationException("InjectableServiceLocator can't be shutdown!"); + } + +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceLocatorProvider.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceLocatorProvider.java new file mode 100644 index 000000000..4fadb6c8b --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceLocatorProvider.java @@ -0,0 +1,81 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.provider; + +import java.util.Collections; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.*; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; + +/** + * Provider for the ServiceLocator in the DI with the correct consumer class. + * + * @author Leon Kiefer + */ +public class ServiceLocatorProvider implements ServiceProvider { + + private SimpleServiceLocator simpleServiceLocator; + + /** + * @param simpleServiceLocator + * used by the created ServiceLocators to resolve Services + */ + public ServiceLocatorProvider(SimpleServiceLocator simpleServiceLocator) { + this.simpleServiceLocator = simpleServiceLocator; + } + + @Override + @Nonnull + public ServiceDescription getServiceDescription() { + return new ServiceDescriptionImpl<>(ServiceLocator.class); + } + + @Override + public ServiceInstantiationDescription getServiceInstantiationDescription( + @Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { + return new ServiceInstantiationDescriptionImpl<>(this.getServiceDescription(), + Collections.singletonMap("consumerClass", + locator.getContextProvider("class").getContext(serviceConsumer)), + InjectableServiceLocator.class); + } + + @Override + @Nonnull + public ServiceLocator createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + return new InjectableServiceLocator(this.simpleServiceLocator, + (Class) serviceInstantiationDescription.getContext().get("consumerClass")); + } + + @Override + public void dispose(@Nonnull ServiceLocator service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + // nothing to do here + } + +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/SingletonServiceProvider.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/SingletonServiceProvider.java index c7a6b19fb..3ed2a8196 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/SingletonServiceProvider.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/SingletonServiceProvider.java @@ -27,10 +27,11 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.ContextLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceInstantiationDescription; import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; /** * A ServiceProvider which provides only a single existing instance @@ -63,20 +64,21 @@ public SingletonServiceProvider(@Nonnull Class serviceType, @Nonnull T instan } @Override - public ServiceImplementationDescription getServiceImplementationDescription(@Nonnull ContextLocator locator, + public ServiceInstantiationDescription getServiceInstantiationDescription(@Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { - return new ServiceImplementationDescriptionImpl<>(serviceConsumer.getServiceDescription(), + return new ServiceInstantiationDescriptionImpl<>(serviceConsumer.getServiceDescription(), this.instance.getClass()); } @Override - public @Nonnull ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { - return new ServiceHandleImpl<>(this.instance); + public @Nonnull T createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + return this.instance; } @Override - public void dispose(ServiceHandle service) { + public void dispose(@Nonnull T service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { // singleton can not be disposed } diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceDescriptionImpl.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceDescriptionImpl.java similarity index 84% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceDescriptionImpl.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceDescriptionImpl.java index 9e25ffb20..5071914f9 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceDescriptionImpl.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceDescriptionImpl.java @@ -21,13 +21,15 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di; +package de.unistuttgart.iaas.amyassist.amy.core.di.runtime; import java.lang.annotation.Annotation; import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; + /** * Implementation of ServiceDescription interface * @@ -42,8 +44,9 @@ public class ServiceDescriptionImpl implements ServiceDescription { /** * @param serviceType + * the type of the Service given as class * @param annotations - * + * the qualifier annotations */ public ServiceDescriptionImpl(Class serviceType, Set annotations) { this.serviceType = serviceType; @@ -51,7 +54,10 @@ public ServiceDescriptionImpl(Class serviceType, Set annotations) } /** - * @param serviceType2 + * Create a Service description with no qualifiers + * + * @param serviceType + * the type of the Service given as class */ public ServiceDescriptionImpl(Class serviceType) { this.serviceType = serviceType; diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceImplementationDescriptionImpl.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceInstantiationDescriptionImpl.java similarity index 68% rename from di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceImplementationDescriptionImpl.java rename to di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceInstantiationDescriptionImpl.java index 4f80a9f31..e96e0034d 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/provider/ServiceImplementationDescriptionImpl.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/runtime/ServiceInstantiationDescriptionImpl.java @@ -21,7 +21,7 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.core.di.provider; +package de.unistuttgart.iaas.amyassist.amy.core.di.runtime; import java.util.Collections; import java.util.Map; @@ -29,16 +29,16 @@ import javax.annotation.Nonnull; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceInstantiationDescription; /** - * Immutable implementation of the ServiceImplementationDescription + * Immutable implementation of the ServiceInstantiationDescription * * @author Leon Kiefer * @param * the type of the service */ -public class ServiceImplementationDescriptionImpl implements ServiceImplementationDescription { +public class ServiceInstantiationDescriptionImpl implements ServiceInstantiationDescription { @Nonnull private final ServiceDescription serviceDescription; @Nonnull @@ -48,10 +48,13 @@ public class ServiceImplementationDescriptionImpl implements ServiceImplement /** * @param serviceDescription + * {@link #getServiceDescription()} * @param context + * {@link #getContext()} * @param cls + * the implementation of the Service {@link #getImplementationClass()} */ - public ServiceImplementationDescriptionImpl(@Nonnull ServiceDescription serviceDescription, + public ServiceInstantiationDescriptionImpl(@Nonnull ServiceDescription serviceDescription, @Nonnull Map context, @Nonnull Class cls) { this.serviceDescription = serviceDescription; this.context = context; @@ -59,12 +62,15 @@ public ServiceImplementationDescriptionImpl(@Nonnull ServiceDescription servi } /** - * Create a new ServiceImplementationDescription with a empty context. + * Create a new ServiceInstantiationDescription with a empty context. * * @param serviceDescription + * {@link #getServiceDescription()} * @param cls + * the implementation of the Service {@link #getImplementationClass()} */ - public ServiceImplementationDescriptionImpl(@Nonnull ServiceDescription serviceDescription, @Nonnull Class cls) { + public ServiceInstantiationDescriptionImpl(@Nonnull ServiceDescription serviceDescription, + @Nonnull Class cls) { this.serviceDescription = serviceDescription; this.context = Collections.emptyMap(); this.cls = cls; diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ConfigurationUtil.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ConfigurationUtil.java new file mode 100644 index 000000000..a82c00570 --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ConfigurationUtil.java @@ -0,0 +1,84 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.util; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.Configuration; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.ClassIsNotAServiceException; +import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ClassServiceProvider; + +/** + * Utility for the registration of classes with the ClassServiceProvider. + * + * @author Leon Kiefer + */ +public class ConfigurationUtil { + + private ConfigurationUtil() { + // hide constructor + } + + /** + * + * @param cls + * the implementation class for which the ClassServiceProvider should be created + * @return the ClassServiceProvider which provides Services of the given class + * @throws ClassIsNotAServiceException + * if the @Service annotation is not present on the given class + * @see Configuration#register(Class) + */ + public static ClassServiceProvider getClassServiceProvider(@Nonnull Class cls) { + Service annotation = cls.getAnnotation(Service.class); + if (annotation == null) + throw new ClassIsNotAServiceException(cls); + @Nonnull + Class serviceType = annotation.value(); + if (serviceType.equals(Void.class)) { + Class[] interfaces = cls.getInterfaces(); + if (interfaces.length == 1) { + // annotations can not have null values + serviceType = interfaces[0]; + } else if (interfaces.length == 0) { + serviceType = cls; + } else { + throw new IllegalArgumentException("The type of the service implementation " + cls.getName() + + " is ambiguous, because the type is not given by the annotation and multiple interfaces are implemented." + + " Please specify which type this service should have."); + } + } + return registerClass(cls, serviceType); + } + + private static ClassServiceProvider registerClass(@Nonnull Class cls, @Nonnull Class serviceType) { + if (!serviceType.isAssignableFrom(cls)) { + throw new IllegalArgumentException( + "The specified service type " + serviceType.getName() + " is not assignable from " + cls.getName()); + } + @SuppressWarnings("unchecked") + Class implementationClass = (Class) cls; + return new ClassServiceProvider<>(serviceType, implementationClass); + } +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ServiceLocatorUtil.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ServiceLocatorUtil.java new file mode 100644 index 000000000..f98a1d24d --- /dev/null +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/ServiceLocatorUtil.java @@ -0,0 +1,90 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.util; + +import java.lang.reflect.Field; + +import javax.annotation.Nonnull; + +import org.apache.commons.lang3.reflect.FieldUtils; + +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ConsumerFactory; + +/** + * Implementation of operations using the SimpleServiceLocator. + * + * @author Leon Kiefer + */ +public class ServiceLocatorUtil { + private ServiceLocatorUtil() { + // hide constructor + } + + /** + * + * @param + * @param serviceClass + * @param locator + * @return the created instance of the given class + * @see ServiceLocator#createAndInitialize(Class) + */ + public static T createAndInitialize(@Nonnull Class serviceClass, @Nonnull SimpleServiceLocator locator) { + if (!Util.isValidServiceClass(serviceClass)) { + throw new IllegalArgumentException( + "There is a problem with the class " + serviceClass.getName() + ". It can't be used as a Service"); + } + T newInstance; + try { + newInstance = serviceClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new IllegalStateException( + "The constructor of " + serviceClass.getName() + " should have been checked", e); + } + inject(newInstance, locator); + Util.postConstruct(newInstance); + + return newInstance; + } + + /** + * @param instance + * @param locator + * @see ServiceLocator#inject(Object) + */ + public static void inject(@Nonnull Object instance, @Nonnull SimpleServiceLocator locator) { + Class classOfInstance = instance.getClass(); + Field[] dependencyFields = FieldUtils.getFieldsWithAnnotation(classOfInstance, Reference.class); + for (Field field : dependencyFields) { + ServiceDescription serviceDescription = Util.serviceDescriptionFor(field); + serviceDescription.getAnnotations().removeIf(annotation -> annotation instanceof Reference); + Class declaredClass = field.getDeclaringClass(); + Object object = locator.getService(ConsumerFactory.build(declaredClass, serviceDescription)).getService(); + Util.inject(instance, object, field); + } + } +} diff --git a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/Util.java b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/Util.java index 7780cb1b3..a9fd809ce 100644 --- a/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/Util.java +++ b/di/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/di/util/Util.java @@ -38,11 +38,11 @@ import org.apache.commons.lang3.reflect.MethodUtils; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Context; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PreDestroy; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; /** * Util for checks and java reflection diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionProviderTest.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionProviderTest.java new file mode 100644 index 000000000..2ba1d91c3 --- /dev/null +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionProviderTest.java @@ -0,0 +1,97 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di; + +import static org.junit.jupiter.api.Assertions.*; + +import javax.annotation.Nonnull; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; +import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; +import uk.org.lidalia.slf4jtest.TestLoggerFactory; + +/** + * Test for DependencyInjection + * + * @author Leon Kiefer + */ +class DependencyInjectionProviderTest { + + private DependencyInjection dependencyInjection; + + @BeforeEach + public void setup() { + this.dependencyInjection = new DependencyInjection(); + } + + @Test + void testServiceAnnotation() { + this.dependencyInjection.getConfiguration().register(new ServiceProvider() { + + @Override + @Nonnull + public ServiceDescription getServiceDescription() { + return new ServiceDescriptionImpl<>(ServiceWithServiceLocator.class); + } + + @Override + public ServiceInstantiationDescription getServiceInstantiationDescription( + @Nonnull ContextLocator locator, + @Nonnull ServiceConsumer serviceConsumer) { + return new ServiceInstantiationDescriptionImpl<>(this.getServiceDescription(), + ServiceWithServiceLocator.class); + } + + @Override + @Nonnull + public ServiceWithServiceLocator createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + return new ServiceWithServiceLocator(locator); + } + + @Override + public void dispose(@Nonnull ServiceWithServiceLocator service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { + } + }); + ServiceWithServiceLocator service = this.dependencyInjection.getServiceLocator() + .getService(ServiceWithServiceLocator.class); + + ServiceConsumer serviceConsumer = Mockito.mock(ServiceConsumer.class); + assertThrows(RuntimeException.class, () -> service.getLocator().getService(serviceConsumer)); + } + + @AfterEach + public void clearLoggers() { + TestLoggerFactory.clear(); + } + +} diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionScopeTest.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionScopeTest.java index 43a73b7f3..4befdb700 100644 --- a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionScopeTest.java +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionScopeTest.java @@ -45,6 +45,8 @@ */ public class DependencyInjectionScopeTest { private DependencyInjection dependencyInjection; + private Configuration configuration; + private ServiceLocator serviceLocator; /** * Setup @@ -52,6 +54,8 @@ public class DependencyInjectionScopeTest { @BeforeEach public void setup() { this.dependencyInjection = new DependencyInjection(); + this.configuration = this.dependencyInjection.getConfiguration(); + this.serviceLocator = this.dependencyInjection.getServiceLocator(); } // Scope GLOBAL and once are already being tested by the other test. @@ -61,14 +65,14 @@ public void setup() { */ @Test public void testScopeClass() { - this.dependencyInjection.register(Service11.class); - this.dependencyInjection.register(Service12.class); - this.dependencyInjection.register(Service13.class); + this.configuration.register(Service11.class); + this.configuration.register(Service12.class); + this.configuration.register(Service13.class); - Service11 s1 = this.dependencyInjection.getService(Service11.class); + Service11 s1 = this.serviceLocator.getService(Service11.class); s1.s.id = 4; - Service13 s2 = this.dependencyInjection.getService(Service13.class); + Service13 s2 = this.serviceLocator.getService(Service13.class); assertThat(s1.s, not(theInstance(s2.s1))); assertThat(s1, theInstance(s2.s2)); assertThat(s1.s, theInstance(s2.s2.s)); @@ -79,13 +83,13 @@ public void testScopeClass() { */ @Test public void testScopePlugin() { - this.dependencyInjection.register(Service14.class); - this.dependencyInjection.register(Service15.class); - this.dependencyInjection.register(Service16.class); - this.dependencyInjection.register(ServiceForPlugins.class); + this.configuration.register(Service14.class); + this.configuration.register(Service15.class); + this.configuration.register(Service16.class); + this.configuration.register(ServiceForPlugins.class); Map, Integer> plugins = new HashMap<>(); - this.dependencyInjection.registerContextProvider("custom", new CustomProvider<>(plugins)); + this.configuration.registerContextProvider("custom", new CustomProvider<>(plugins)); Class cls1 = Service14.class; Class cls2 = Service15.class; @@ -95,10 +99,10 @@ public void testScopePlugin() { plugins.put(cls2, 1); plugins.put(cls3, 2); - Service14 s1 = this.dependencyInjection.getService(Service14.class); - Service15 s2 = this.dependencyInjection.getService(Service15.class); + Service14 s1 = this.serviceLocator.getService(Service14.class); + Service15 s2 = this.serviceLocator.getService(Service15.class); - Service16 s3 = this.dependencyInjection.getService(Service16.class); + Service16 s3 = this.serviceLocator.getService(Service16.class); assertThat(s1.s, theInstance(s2.s)); assertThat(s1.s, not(theInstance(s3.s))); @@ -106,42 +110,42 @@ public void testScopePlugin() { @Test public void testContextValue() { - this.dependencyInjection.register(Service11.class); - this.dependencyInjection.register(Service12.class); - this.dependencyInjection.register(Service13.class); + this.configuration.register(Service11.class); + this.configuration.register(Service12.class); + this.configuration.register(Service13.class); - Service11 s1 = this.dependencyInjection.getService(Service11.class); + Service11 s1 = this.serviceLocator.getService(Service11.class); assertThat(s1.s.getConsumerClass(), notNullValue()); assertThat(s1.s.getConsumerClass(), equalTo(Service11.class)); } @Test public void testInjectContextValue() { - this.dependencyInjection.register(Service12.class); + this.configuration.register(Service12.class); Service11 s1 = new Service11(); - this.dependencyInjection.inject(s1); + this.serviceLocator.inject(s1); assertThat(s1.s.getConsumerClass(), notNullValue()); assertThat(s1.s.getConsumerClass(), equalTo(Service11.class)); } @Test public void testNoContextProvider() { - this.dependencyInjection.register(Service16.class); - this.dependencyInjection.register(ServiceForPlugins.class); + this.configuration.register(Service16.class); + this.configuration.register(ServiceForPlugins.class); String message = assertThrows(NoSuchElementException.class, - () -> this.dependencyInjection.getService(Service16.class)).getMessage(); + () -> this.serviceLocator.getService(Service16.class)).getMessage(); assertThat(message, equalTo("custom")); } @Test public void testContextProviderWithAbstractClass() { - this.dependencyInjection.register(Service12.class); - this.dependencyInjection.register(ServiceTemplate1.class); + this.configuration.register(Service12.class); + this.configuration.register(ServiceTemplate1.class); - ServiceTemplate1 service = this.dependencyInjection.getService(ServiceTemplate1.class); + ServiceTemplate1 service = this.serviceLocator.getService(ServiceTemplate1.class); assertThat(service.getService12(), is(notNullValue())); assertThat(service.getService12FromAbstractServiceTemplate(), is(notNullValue())); diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionTest.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionTest.java index f596371e5..31ae55feb 100644 --- a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionTest.java +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/DependencyInjectionTest.java @@ -33,6 +33,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.ClassIsNotAServiceException; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.DuplicateServiceException; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.ServiceNotFoundException; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.SingletonServiceProvider; import uk.org.lidalia.slf4jtest.TestLogger; @@ -46,57 +49,61 @@ class DependencyInjectionTest { private DependencyInjection dependencyInjection; + private Configuration configuration; + private ServiceLocator serviceLocator; @BeforeEach public void setup() { this.dependencyInjection = new DependencyInjection(); - this.dependencyInjection.register(Service1.class); - this.dependencyInjection.register(Service2.class); - this.dependencyInjection.register(Service3.class); + this.configuration = this.dependencyInjection.getConfiguration(); + this.serviceLocator = this.dependencyInjection.getServiceLocator(); + this.configuration.register(Service1.class); + this.configuration.register(Service2.class); + this.configuration.register(Service3.class); } @Test void testServiceAnnotation() { - Service1 service1 = this.dependencyInjection.getService(Service1.class); + Service1 service1 = this.serviceLocator.getService(Service1.class); assertThat(service1, is(instanceOf(Service1.class))); } @Test void testDependencyInjection() { - Service2 service2 = this.dependencyInjection.getService(Service2.class); + Service2 service2 = this.serviceLocator.getService(Service2.class); assertThat(service2.checkServices(), is(true)); } @Test void testServiceRegistry() { - Service1 s1 = this.dependencyInjection.getService(Service1.class); - Service1 s2 = this.dependencyInjection.getService(Service1.class); + Service1 s1 = this.serviceLocator.getService(Service1.class); + Service1 s2 = this.serviceLocator.getService(Service1.class); assertThat(s1, theInstance(s2)); } @Test void testCircularDependencies() { - this.dependencyInjection.register(Service4.class); - this.dependencyInjection.register(Service5.class); + this.configuration.register(Service4.class); + this.configuration.register(Service5.class); assertTimeoutPreemptively(Duration.ofSeconds(1), () -> { - assertThrows(RuntimeException.class, () -> this.dependencyInjection.getService(Service4.class)); + assertThrows(RuntimeException.class, () -> this.serviceLocator.getService(Service4.class)); }); } @Test() void testRegisterNotAService() { String message = assertThrows(ClassIsNotAServiceException.class, - () -> this.dependencyInjection.register(NotAService.class)).getMessage(); + () -> this.configuration.register(NotAService.class)).getMessage(); assertThat(message, equalTo("The class " + NotAService.class.getName() + " is not a Service")); } @Test() void testServiceNotFoundException() { - this.dependencyInjection.register(Service6.class); + this.configuration.register(Service6.class); String message = assertThrows(ServiceNotFoundException.class, - () -> this.dependencyInjection.getService(Service6.class)).getMessage(); + () -> this.serviceLocator.getService(Service6.class)).getMessage(); assertThat(message, equalTo("No Service of type " + Service7API.class.getName() + " with qualifier [] is registered in the DI." + "\nRequired by:" @@ -114,21 +121,21 @@ void testServiceNotFoundException() { @Test() void testDuplicateServiceException() { - this.dependencyInjection.register(Service6.class); - assertThrows(DuplicateServiceException.class, () -> this.dependencyInjection.register(Service6.class)); + this.configuration.register(Service6.class); + assertThrows(DuplicateServiceException.class, () -> this.configuration.register(Service6.class)); } @Test() void testDuplicateServiceException2() { - this.dependencyInjection.register(new SingletonServiceProvider<>(Service7API.class, new Service7())); - assertThrows(DuplicateServiceException.class, () -> this.dependencyInjection - .register(new SingletonServiceProvider<>(Service7API.class, new Service7()))); + this.configuration.register(new SingletonServiceProvider<>(Service7API.class, new Service7())); + assertThrows(DuplicateServiceException.class, + () -> this.configuration.register(new SingletonServiceProvider<>(Service7API.class, new Service7()))); } @Test() void testConstructorCheck() { String message = assertThrows(IllegalArgumentException.class, - () -> this.dependencyInjection.register(ServiceWithConstructor.class)).getMessage(); + () -> this.configuration.register(ServiceWithConstructor.class)).getMessage(); assertThat(message, equalTo("There is a problem with the class " + ServiceWithConstructor.class.getName() + ". It can't be used as a Service")); @@ -138,49 +145,48 @@ void testConstructorCheck() { void testServiceWithDuplicateDependency() { TestLogger logger = TestLoggerFactory.getTestLogger(ServiceProvider.class); - this.dependencyInjection.register(ServiceWithDuplicateDependency.class); - ServiceWithDuplicateDependency service = this.dependencyInjection - .getService(ServiceWithDuplicateDependency.class); + this.configuration.register(ServiceWithDuplicateDependency.class); + ServiceWithDuplicateDependency service = this.serviceLocator.getService(ServiceWithDuplicateDependency.class); assertThat(service, is(notNullValue())); } @Test() void testPostConstruct() { - Service1 service1 = this.dependencyInjection.getService(Service1.class); + Service1 service1 = this.serviceLocator.getService(Service1.class); assertThat(service1.init, is(1)); - this.dependencyInjection.postConstruct(service1); + this.serviceLocator.postConstruct(service1); assertThat(service1.init, is(2)); } @Test() void testCreateAndInitializeService() { - Service2 s2_1 = this.dependencyInjection.getService(Service2.class); - Service2 s2_2 = this.dependencyInjection.createAndInitialize(Service2.class); + Service2 s2_1 = this.serviceLocator.getService(Service2.class); + Service2 s2_2 = this.serviceLocator.createAndInitialize(Service2.class); assertThat(s2_1, not(theInstance(s2_2))); assertThat(s2_1.getService3(), theInstance(s2_2.getService3())); } @Test() void testCreateAndInitializeServicePostConstruct() { - Service18 service18 = this.dependencyInjection.createAndInitialize(Service18.class); + Service18 service18 = this.serviceLocator.createAndInitialize(Service18.class); assertThat(service18.setup, is(true)); } @Test() void testCreateNotAService() { - NotAService2 nas = this.dependencyInjection.createAndInitialize(NotAService2.class); + NotAService2 nas = this.serviceLocator.createAndInitialize(NotAService2.class); assertThat(nas.getInit(), is(1)); - Service1 s1 = this.dependencyInjection.getService(Service1.class); - this.dependencyInjection.postConstruct(s1); + Service1 s1 = this.serviceLocator.getService(Service1.class); + this.serviceLocator.postConstruct(s1); assertThat(nas.getInit(), is(2)); - NotAService2 nas2 = this.dependencyInjection.createAndInitialize(NotAService2.class); + NotAService2 nas2 = this.serviceLocator.createAndInitialize(NotAService2.class); assertThat(nas2, not(theInstance(nas))); } @Test() void testCreateIllegalAccessException() { String message = assertThrows(IllegalArgumentException.class, - () -> this.dependencyInjection.createAndInitialize(Service8.class)).getMessage(); + () -> this.serviceLocator.createAndInitialize(Service8.class)).getMessage(); assertThat(message, equalTo( "There is a problem with the class " + Service8.class.getName() + ". It can't be used as a Service")); @@ -189,19 +195,19 @@ void testCreateIllegalAccessException() { @Test() void testSingletonServiceProvider() { Service7 service7 = new Service7(); - this.dependencyInjection.register(new SingletonServiceProvider<>(Service7API.class, service7)); - assertThat(this.dependencyInjection.getService(Service7API.class), is(theInstance(service7))); + this.configuration.register(new SingletonServiceProvider<>(Service7API.class, service7)); + assertThat(this.serviceLocator.getService(Service7API.class), is(theInstance(service7))); } @Test() void testAbstractService() { - assertThrows(IllegalArgumentException.class, () -> this.dependencyInjection.register(AbstractService.class)); + assertThrows(IllegalArgumentException.class, () -> this.configuration.register(AbstractService.class)); } @Test() void testInject() { Service10 service10 = new Service10(10); - this.dependencyInjection.inject(service10); + this.serviceLocator.inject(service10); assertThat(service10.isInit(), is(false)); assertThat(service10.getService1(), is(notNullValue())); assertThat(service10.getService1(), is(instanceOf(Service1.class))); @@ -209,24 +215,24 @@ void testInject() { @Test() void testServiceType() { - assertThrows(IllegalArgumentException.class, () -> this.dependencyInjection.register(Service17.class)); + assertThrows(IllegalArgumentException.class, () -> this.configuration.register(Service17.class)); } @Test() void testRegisterServiceTypeFromInterface() { - this.dependencyInjection.register(Service7Impl.class); - assertThat(this.dependencyInjection.getService(Service7API.class), is(notNullValue())); + this.configuration.register(Service7Impl.class); + assertThat(this.serviceLocator.getService(Service7API.class), is(notNullValue())); } @Test() void testRegisterServiceTypeFromMultipleInterfaces() { assertThrows(IllegalArgumentException.class, - () -> this.dependencyInjection.register(ServiceImplementingMultipleInterfaces.class)); + () -> this.configuration.register(ServiceImplementingMultipleInterfaces.class)); } @Test() void testCreateAndInitialize() { - Service1 service1 = this.dependencyInjection.createAndInitialize(Service1.class); + Service1 service1 = this.serviceLocator.createAndInitialize(Service1.class); assertThat(service1, notNullValue()); assertThat(service1.init, is(1)); } diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceWithServiceLocator.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceWithServiceLocator.java new file mode 100644 index 000000000..e78060ea6 --- /dev/null +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/ServiceWithServiceLocator.java @@ -0,0 +1,46 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di; + +/** + * Test Service to test the use of the ServiceLocator. + * + * @author Leon Kiefer + */ +public class ServiceWithServiceLocator { + + private SimpleServiceLocator locator; + + public SimpleServiceLocator getLocator() { + return this.locator; + } + + /** + * @param locator + */ + public ServiceWithServiceLocator(SimpleServiceLocator locator) { + this.locator = locator; + } + +} diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/context/provider/TestStaticProvider.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/context/provider/TestStaticProvider.java index 022e71e07..ecb0ec648 100644 --- a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/context/provider/TestStaticProvider.java +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/context/provider/TestStaticProvider.java @@ -45,15 +45,16 @@ public void setup() { @Test void testUseAnnoations() { - this.dependencyInjection.register(ServiceWithAnnotationContext.class); - this.dependencyInjection.register(ServiceWithDependencies.class); + this.dependencyInjection.getConfiguration().register(ServiceWithAnnotationContext.class); + this.dependencyInjection.getConfiguration().register(ServiceWithDependencies.class); - this.dependencyInjection.registerContextProvider("annotation", + this.dependencyInjection.getConfiguration().registerContextProvider("annotation", consumer -> consumer.getServiceDescription().getAnnotations().stream() .filter(annotation -> annotation instanceof AnnotatoinWithValue).findFirst() .map(annotation -> ((AnnotatoinWithValue) annotation).value()).orElse(null)); - ServiceWithDependencies service = this.dependencyInjection.getService(ServiceWithDependencies.class); + ServiceWithDependencies service = this.dependencyInjection.getServiceLocator() + .getService(ServiceWithDependencies.class); assertThat(service.getValueOfService1(), is("1")); assertThat(service.getValueOfService2(), is("2")); } diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ExtensionTest.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ExtensionTest.java index 31b1bf22d..6cb80436e 100644 --- a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ExtensionTest.java +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ExtensionTest.java @@ -55,7 +55,17 @@ void testPostConstruct() { @Test void testOnRegister() { - this.dependencyInjection.register(Service1.class); + this.dependencyInjection.getConfiguration().register(Service1.class); + + Mockito.verify(this.mockExtension).onRegister(ArgumentMatchers.any(), ArgumentMatchers.eq(Service1.class)); + } + + @Test + void testOnRegisterFromService() { + this.dependencyInjection.getConfiguration().register(ServiceConfigurationChange.class); + Mockito.verify(this.mockExtension).onRegister(ArgumentMatchers.any(), + ArgumentMatchers.eq(ServiceConfigurationChange.class)); + this.dependencyInjection.getServiceLocator().getService(ServiceConfigurationChange.class).install(); Mockito.verify(this.mockExtension).onRegister(ArgumentMatchers.any(), ArgumentMatchers.eq(Service1.class)); } diff --git a/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ServiceConfigurationChange.java b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ServiceConfigurationChange.java new file mode 100644 index 000000000..994d2b72f --- /dev/null +++ b/di/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/di/extension/ServiceConfigurationChange.java @@ -0,0 +1,45 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.di.extension; + +import de.unistuttgart.iaas.amyassist.amy.core.di.Configuration; +import de.unistuttgart.iaas.amyassist.amy.core.di.Service1; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; + +/** + * Test Service which uses Configuration + * + * @author Leon Kiefer + */ +@Service +public class ServiceConfigurationChange { + @Reference + private Configuration configuration; + + public void install() { + this.configuration.register(Service1.class); + } + +} diff --git a/http-server-api/pom.xml b/http-server-api/pom.xml index 793101f9d..8660322ea 100644 --- a/http-server-api/pom.xml +++ b/http-server-api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml \ No newline at end of file diff --git a/http-server/pom.xml b/http-server/pom.xml index 25e3f1cac..3c734a6af 100644 --- a/http-server/pom.xml +++ b/http-server/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationMessageBodyWriter.java b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationMessageBodyWriter.java new file mode 100644 index 000000000..947bd2f76 --- /dev/null +++ b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationMessageBodyWriter.java @@ -0,0 +1,70 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.httpserver.adapter; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.time.Duration; + +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.MultivaluedMap; +import javax.ws.rs.ext.MessageBodyWriter; +import javax.ws.rs.ext.Provider; + +/** + * A Duration message body writer to write OutputStreams according to ISO-8601 + * + * @author Muhammed Kaya + */ +@Provider +@Produces(MediaType.TEXT_PLAIN) +public class DurationMessageBodyWriter implements MessageBodyWriter { + + /** + * @see javax.ws.rs.ext.MessageBodyWriter#isWriteable(java.lang.Class, java.lang.reflect.Type, + * java.lang.annotation.Annotation[], javax.ws.rs.core.MediaType) + */ + @Override + public boolean isWriteable(Class type, Type genericType, Annotation[] annotations, MediaType mediaType) { + return type.equals(Duration.class); + } + + /** + * @see javax.ws.rs.ext.MessageBodyWriter#writeTo(java.lang.Object, java.lang.Class, java.lang.reflect.Type, + * java.lang.annotation.Annotation[], javax.ws.rs.core.MediaType, javax.ws.rs.core.MultivaluedMap, + * java.io.OutputStream) + */ + @Override + public void writeTo(Duration d, Class type, Type genericType, Annotation[] annotations, MediaType mediaType, + MultivaluedMap httpHeaders, OutputStream entityStream) throws IOException { + OutputStreamWriter writer = new OutputStreamWriter(entityStream, "ISO-8859-1"); + writer.write(d.toString()); + writer.flush(); + writer.close(); + } +} diff --git a/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationParameterConverter.java b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationParameterConverter.java new file mode 100644 index 000000000..f1bc8ab59 --- /dev/null +++ b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationParameterConverter.java @@ -0,0 +1,52 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.httpserver.adapter; + +import java.time.Duration; + +import javax.ws.rs.ext.ParamConverter; + +/** + * A Duration parameter converter to parse and get Strings according to ISO-8601 + * + * @author Muhammed Kaya + */ +public class DurationParameterConverter implements ParamConverter { + + /** + * @see javax.ws.rs.ext.ParamConverter#fromString(java.lang.String) + */ + @Override + public Duration fromString(String value) { + return Duration.parse(value); + } + + /** + * @see javax.ws.rs.ext.ParamConverter#toString(java.lang.Object) + */ + @Override + public String toString(Duration value) { + return value.toString(); + } +} diff --git a/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationProvider.java b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationProvider.java new file mode 100644 index 000000000..4dbe3a82b --- /dev/null +++ b/http-server/src/main/java/de/unistuttgart/iaas/amyassist/amy/httpserver/adapter/DurationProvider.java @@ -0,0 +1,51 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.httpserver.adapter; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.time.Duration; + +import javax.ws.rs.ext.ParamConverter; +import javax.ws.rs.ext.ParamConverterProvider; + +/** + * A Duration provider to return DurationParameterConverter according to ISO-8601 + * + * @author Muhammed Kaya + */ +public class DurationProvider implements ParamConverterProvider { + + /** + * @see javax.ws.rs.ext.ParamConverterProvider#getConverter(java.lang.Class, java.lang.reflect.Type, + * java.lang.annotation.Annotation[]) + */ + @Override + public ParamConverter getConverter(Class rawType, Type genericType, Annotation[] annotations) { + if (rawType.equals(Duration.class)) { + return (ParamConverter) new DurationParameterConverter(); + } + return null; + } +} diff --git a/http-server/src/test/java/de/unistuttgart/iaas/amyassist/amy/httpserver/ServerTest.java b/http-server/src/test/java/de/unistuttgart/iaas/amyassist/amy/httpserver/ServerTest.java index 9b0e0f342..b80aa68fb 100644 --- a/http-server/src/test/java/de/unistuttgart/iaas/amyassist/amy/httpserver/ServerTest.java +++ b/http-server/src/test/java/de/unistuttgart/iaas/amyassist/amy/httpserver/ServerTest.java @@ -38,6 +38,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.configuration.ConfigurationManager; import de.unistuttgart.iaas.amyassist.amy.core.di.DependencyInjection; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.SingletonServiceProvider; /** @@ -47,6 +48,7 @@ class ServerTest { private DependencyInjection dependencyInjection; + private ServiceLocator locator; @BeforeEach void setup() { @@ -60,14 +62,17 @@ void setup() { Mockito.when(configManager.getConfigurationWithDefaults(ServerImpl.CONFIG_NAME)).thenReturn(serverConfig); this.dependencyInjection = new DependencyInjection(); - this.dependencyInjection.register(new SingletonServiceProvider<>(ConfigurationManager.class, configManager)); - this.dependencyInjection.register(new SingletonServiceProvider<>(Logger.class, LoggerFactory.getLogger(ServerImpl.class))); + this.locator = this.dependencyInjection.getServiceLocator(); + this.dependencyInjection.getConfiguration() + .register(new SingletonServiceProvider<>(ConfigurationManager.class, configManager)); + this.dependencyInjection.getConfiguration() + .register(new SingletonServiceProvider<>(Logger.class, LoggerFactory.getLogger(ServerImpl.class))); } @Test void test() { assertTimeout(ofSeconds(2), () -> { - ServerImpl server = this.dependencyInjection.createAndInitialize(ServerImpl.class); + ServerImpl server = this.locator.createAndInitialize(ServerImpl.class); server.startWithResources(TestRestResource.class); server.stop(); }, "The Server start and shotdown takes longer then 2 Seconds"); @@ -76,7 +81,7 @@ void test() { @Test void testCantStartServerTwice() { - ServerImpl server = this.dependencyInjection.createAndInitialize(ServerImpl.class); + ServerImpl server = this.locator.createAndInitialize(ServerImpl.class); String message = assertThrows(IllegalStateException.class, () -> { server.startWithResources(TestRestResource.class); server.startWithResources(TestRestResource.class); @@ -87,7 +92,7 @@ void testCantStartServerTwice() { @Test void testRegister() { - ServerImpl server = this.dependencyInjection.createAndInitialize(ServerImpl.class); + ServerImpl server = this.locator.createAndInitialize(ServerImpl.class); server.register(TestRestResource.class); server.start(); server.stop(); @@ -95,7 +100,7 @@ void testRegister() { @Test void testRegisterNonResourceClass() { - ServerImpl server = this.dependencyInjection.createAndInitialize(ServerImpl.class); + ServerImpl server = this.locator.createAndInitialize(ServerImpl.class); assertThrows(IllegalArgumentException.class, () -> { server.register(ServerTest.class); }, "The Server dont throw an IllegalArgumentException if a registered class is not a Rest Resource"); diff --git a/natlang-api/pom.xml b/natlang-api/pom.xml index 4b877f3ab..97ae13431 100644 --- a/natlang-api/pom.xml +++ b/natlang-api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandler.java b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandler.java index 94de8cf34..33f82bdce 100644 --- a/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandler.java +++ b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandler.java @@ -45,7 +45,7 @@ public interface DialogHandler { * callback for consumer * @return the matching uuid */ - UUID createDialog(Consumer cons); + UUID createDialog(Consumer cons); /** * deletes a save dialog - this should be called once in a while if the core is running for any long amounts of time diff --git a/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformation.java b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformation.java new file mode 100644 index 000000000..0c1388242 --- /dev/null +++ b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformation.java @@ -0,0 +1,55 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.natlang; + +import java.util.List; + +/** + * Natlang information interface provides informations about agf grammars beeing used in natural language recognition + * + * @author Felix Burk + */ +public interface NatlangInformation { + + /** + * provides possible sentences that contain a keyword + * + * @param keyword + * the sentence contains + * @param nmbSentences + * how many sentences sahould be provided at max + * @return a list of sentences + */ + public List getSampleSentencesFromKeyword(String keyword, int nmbSentences); + + /** + * provides any number of possible sentences + * + * @param nmbSentences + * how many sentences should be provided + * @return a list of sentences + */ + public List getAnySampleSentences(int nmbSentences); + +} diff --git a/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/Response.java b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/Response.java new file mode 100644 index 000000000..13ebfc9da --- /dev/null +++ b/natlang-api/src/main/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/Response.java @@ -0,0 +1,128 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.natlang; + +/** + * + * Class that represents a rich response. + * A 'rich response' can contain a text message and additional metadata. + * + * @author Benno Krauß + */ +public class Response { + + /* + ATTRIBUTES + */ + + private String text; + private String widget; + private Object attachment; + + /* + BUILDER + */ + + public static class ResponseBuilder { + private String text = null; + private String widget = null; + private Object attachment = null; + + private ResponseBuilder() { + } + + public ResponseBuilder text(String text) { + this.text = text; + return this; + } + + public ResponseBuilder widget(String widget) { + this.widget = widget; + return this; + } + + public ResponseBuilder attachment(Object attachment) { + this.attachment = attachment; + return this; + } + + public Response build() { + return new Response(text, widget, attachment); + } + } + + /* + BUILDER METHODS + */ + + public static ResponseBuilder text(String text) { + return new ResponseBuilder().text(text); + } + + public static ResponseBuilder widget(String widget) { + return new ResponseBuilder().widget(widget); + } + + public static ResponseBuilder attachment(Object attachment) { + return new ResponseBuilder().attachment(attachment); + } + + /* + CONSTRUCTOR + */ + + private Response(String text, String widget, Object attachment) { + this.text = text; + this.widget = widget; + this.attachment = attachment; + } + + private Response() { + } + + /* + GETTERS + */ + + public String getText() { + return text; + } + + public String getWidget() { + return widget; + } + + public Object getAttachment() { + return attachment; + } + + @Override + public String toString() { + return "Response{" + + "text='" + text + '\'' + + ", widget='" + widget + '\'' + + ", attachment='" + attachment + + '}'; + } +} diff --git a/natlang/pom.xml b/natlang/pom.xml index ebf616606..444e6cba6 100644 --- a/natlang/pom.xml +++ b/natlang/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/Dialog.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/Dialog.java index 5b251c029..bbfdfdeec 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/Dialog.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/Dialog.java @@ -25,6 +25,7 @@ import java.util.function.Consumer; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; import de.unistuttgart.iaas.amyassist.amy.natlang.userinteraction.Prompt; import de.unistuttgart.iaas.amyassist.amy.natlang.userinteraction.UserIntent; @@ -35,7 +36,7 @@ */ public class Dialog { - private Consumer consumer; + private Consumer consumer; /** * constructor @@ -43,7 +44,7 @@ public class Dialog { * @param cons * consumer for callback */ - public Dialog(Consumer cons) { + public Dialog(Consumer cons) { this.consumer = cons; } @@ -63,7 +64,7 @@ public Dialog(Consumer cons) { * @param answerOutput * the string to output */ - public void output(String answerOutput) { + public void output(Response answerOutput) { this.consumer.accept(answerOutput); } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/DialogHandlerImpl.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/DialogHandlerImpl.java index d6e4594a6..6de5d64fc 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/DialogHandlerImpl.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/DialogHandlerImpl.java @@ -28,6 +28,7 @@ import java.util.UUID; import java.util.function.Consumer; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; import org.slf4j.Logger; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; @@ -60,7 +61,7 @@ public class DialogHandlerImpl implements DialogHandler { private Map map = new HashMap<>(); @Override - public UUID createDialog(Consumer cons) { + public UUID createDialog(Consumer cons) { UUID uuid = UUID.randomUUID(); this.map.put(uuid, new Dialog(cons)); return uuid; @@ -96,7 +97,12 @@ public void process(String naturalLanguageText, UUID uuid) { } if (intent.getPartialNLIClass() != null) { Object object = this.serviceLocator.createAndInitialize(intent.getPartialNLIClass()); - dialog.output(dialog.getIntent().call(object, stringToEntityData)); + try { + dialog.output(dialog.getIntent().call(object, stringToEntityData)); + } catch (RuntimeException e) { // NOSONAR + this.logger.warn("Error during plugin execution", e); + dialog.output(Response.text("There is an error in the plugin. For more details see in the logs.").build()); + } } dialog.setIntent(null); } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLIAnnotationReader.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLIAnnotationReader.java index 4ca19eba7..f87bac1d8 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLIAnnotationReader.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLIAnnotationReader.java @@ -32,13 +32,9 @@ import javax.annotation.Nonnull; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.*; import org.apache.commons.lang3.reflect.MethodUtils; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProvider; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProviders; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; - /** * This class is responsible to read the annotations of a given class * @@ -123,8 +119,9 @@ public static void assertValid(Method method) { throw new IllegalArgumentException("The method " + method.toString() + " does not have the correct parameter type. It should be a Map."); } - if (!method.getReturnType().equals(String.class)) { - throw new IllegalArgumentException("The returntype of a method annotated with @Intent should be String."); + if (!method.getReturnType().equals(String.class) && !method.getReturnType().equals(Response.class)) { + throw new IllegalArgumentException("The returntype of a method annotated with @Intent should be either " + + "String or Response."); } if (method.getExceptionTypes().length > 0) { throw new IllegalArgumentException("The method annotated with @Intent should should not throw exceptions."); @@ -144,11 +141,16 @@ public static void assertValid(Method method) { * @throws IllegalArgumentException * if the annotated methods not valid */ - public static String callNLIMethod(@Nonnull Method method, @Nonnull Object instance, Object[] arg) { + public static Response callNLIMethod(@Nonnull Method method, @Nonnull Object instance, Object[] arg) { assertValid(method); try { method.setAccessible(true); - return (String) method.invoke(instance, arg); + Object response = method.invoke(instance, arg); + if (response instanceof String) + return Response.text((String)response).build(); + else if (response instanceof Response) + return (Response)response; + throw new IllegalArgumentException("Invalid return type from NLI method: " + response.getClass().toString()); } catch (IllegalAccessException e) { throw new IllegalArgumentException("tryed to invoke method " + method + " but got an error", e); } catch (InvocationTargetException e) { diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManager.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManager.java index dcc2b1176..97a06e44d 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManager.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManager.java @@ -24,7 +24,9 @@ package de.unistuttgart.iaas.amyassist.amy.natlang; import java.lang.reflect.Method; +import java.util.List; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode; import de.unistuttgart.iaas.amyassist.amy.natlang.aim.XMLAIMIntent; /** @@ -56,4 +58,10 @@ public interface NLProcessingManager { */ Dialog decideIntent(Dialog dialog, String naturalLanguageText); + /** + * returns a list of possible grammars that will be understood by amy + * @return list of agf nodes + */ + List getPossibleGrammars(); + } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManagerImpl.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManagerImpl.java index f20ba04f0..5ebd92808 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManagerImpl.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NLProcessingManagerImpl.java @@ -24,12 +24,8 @@ package de.unistuttgart.iaas.amyassist.amy.natlang; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; -import java.util.Random; import org.slf4j.Logger; @@ -39,6 +35,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFLexer; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFParser; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode; @@ -86,7 +83,7 @@ public class NLProcessingManagerImpl implements NLProcessingManager { @Reference private ConfigurationManager configurationLoader; - private static final String CONFIG_NAME = "core.config"; + private static final String CONFIG_NAME = "natlang.config"; private static final String PROPERTY_ENABLE_STEMMER = "enableStemmer"; private static final String PROBERTY_LANGUAGE = "chooseLanguage"; @@ -119,7 +116,7 @@ public void register(Method method, XMLAIMIntent intent) { UserIntentTemplate template = new UserIntentTemplate(method, intent); - // unfortunately we have to use UserIntent here because enity data has to be present + // unfortunately we have to use UserIntent here because entity data has to be present UserIntent userIntent = new UserIntent(method, intent); this.nodeToMethodAIMPair.put(userIntent.getGrammar(), template); } @@ -131,22 +128,24 @@ public void register(Method method, XMLAIMIntent intent) { @Override public void processIntent(Dialog dialog, String naturalLanguageText) { NLLexer nlLexer = new NLLexer(this.language); - List tokens = nlLexer.tokenize(naturalLanguageText); + List tokens = nlLexer.tokenize(naturalLanguageText); + if (!promptGrammarFound(dialog, tokens)) { // try to skip prefixes and suffixes until a grammar matches for (int i = 0; i <= tokens.size(); i++) { for (int j = tokens.size(); j > i; j--) { - if (promptGrammarFound(dialog, tokens.subList(i, j))) { + if (tokens.subList(i, j).size() > 2 && promptGrammarFound(dialog, tokens.subList(i, j))) { return; } + } } this.logger.debug("no matching grammar found"); - dialog.output(dialog.getNextPrompt().getOutputText()); + dialog.output(Response.text(dialog.getNextPrompt().getOutputText()).build()); } } - private boolean promptGrammarFound(Dialog dialog, List tokens) { + private boolean promptGrammarFound(Dialog dialog, List tokens) { List promptGrams = new ArrayList<>(); if (dialog.getNextPrompt() != null) { promptGrams.add(dialog.getNextPrompt().getGrammar()); @@ -159,7 +158,7 @@ private boolean promptGrammarFound(Dialog dialog, List tokens) { int matchingNodeIndex = nlParser.matchingNodeIndex(tokens); if (matchingNodeIndex == promptGrams.indexOf(this.quitIntentUserInputGram)) { - dialog.output(generateRandomAnswer(QUIT_INTENT_ANSWER)); + dialog.output(Response.text(generateRandomAnswer(QUIT_INTENT_ANSWER)).build()); dialog.setIntent(null); return true; } @@ -182,7 +181,7 @@ private void setEntities(AGFNode gram, Dialog dialog) { if (!dialog.getIntent().isFinished()) { dialog.setNextPrompt(dialog.getIntent().getNextPrompt()); - dialog.output(dialog.getNextPrompt().getOutputText()); + dialog.output(Response.text(dialog.getNextPrompt().getOutputText()).build()); } } @@ -215,7 +214,7 @@ private Map getEntityContent(AGFNode node) { @Override public Dialog decideIntent(Dialog dialog, String naturalLanguageText) { NLLexer nlLexer = new NLLexer(this.language); - List tokens = nlLexer.tokenize(naturalLanguageText); + List tokens = nlLexer.tokenize(naturalLanguageText); if (intentFound(dialog, tokens)) { return dialog; @@ -224,19 +223,20 @@ public Dialog decideIntent(Dialog dialog, String naturalLanguageText) { // try to skip prefixes and suffixes until a grammar matches for (int i = 0; i <= tokens.size(); i++) { for (int j = tokens.size(); j > i; j--) { - if (intentFound(dialog, tokens.subList(i, j))) { + if (tokens.subList(i, j).size() > 2 && intentFound(dialog, tokens.subList(i, j))) { return dialog; } + } } this.logger.debug("no matching grammar found"); - dialog.output(generateRandomAnswer(FAILED_TO_UNDERSTAND_ANSWER)); + dialog.output(Response.text(generateRandomAnswer(FAILED_TO_UNDERSTAND_ANSWER)).build()); return dialog; } - private boolean intentFound(Dialog dialog, List tokens) { + private boolean intentFound(Dialog dialog, List tokens) { INLParser nlParser = new NLParser(new ArrayList<>(this.nodeToMethodAIMPair.keySet()), this.language.getStemmer()); AGFNode node; @@ -265,4 +265,14 @@ private String generateRandomAnswer(String[] strings) { int rndm = rand.nextInt(strings.length); return strings[rndm]; } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.natlang.NLProcessingManager#getPossibleGrammars() + */ + @Override + public List getPossibleGrammars() { + List list = new ArrayList<>(); + list.addAll(this.nodeToMethodAIMPair.keySet()); + return list; + } } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NatlangInformationImpl.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NatlangInformationImpl.java new file mode 100644 index 000000000..bc50d3544 --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/NatlangInformationImpl.java @@ -0,0 +1,119 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.NatlangInformation; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.WordNode; + +/** + * Service which provides information of possible sentences to tell amy + * + * @author Felix Burk + */ +@Service +public class NatlangInformationImpl implements NatlangInformation { + + @Reference + private NLProcessingManager manager; + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.natlang.NatlangInformation#getSampleSentencesFromKeyword(java.lang.String, + * int) + */ + @Override + public List getSampleSentencesFromKeyword(String keyword, int nmbSentences) { + List grams = this.manager.getPossibleGrammars(); + List result = new ArrayList<>(); + + for (AGFNode node : grams) { + List words = node.getChildWordNodes(); + boolean b = words.stream().anyMatch(w -> w.getContent().equals(keyword)); + if (b) + result.add(generateSentenceFromGrammar(node).trim()); + } + return result; + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.natlang.NatlangInformation#getAnySampleSentences(int) + */ + @Override + public List getAnySampleSentences(int nmbSentences) { + List result = new ArrayList<>(nmbSentences); + List grams = this.manager.getPossibleGrammars(); + Collections.shuffle(grams); + for (AGFNode node : grams) { + result.add(generateSentenceFromGrammar(node).trim()); + } + return result; + } + + /** + * convenience method to generate a valid sentence from a grammar + * + * @param node + * grammar to generate from + * @return a matching string + */ + private String generateSentenceFromGrammar(AGFNode node) { + StringBuilder result = new StringBuilder(); + Random random = new Random(); + + switch (node.getType()) { + case OPG: + boolean b = random.nextInt(2) % 2 == 0; + if (!b) { + return ""; + } + //$FALL-THROUGH$ + case ORG: + int i = random.nextInt(node.getChilds().size()); + return result.append(generateSentenceFromGrammar(node.getChilds().get(i))).toString(); + case SHORTWC: + return result.append(" +").toString(); + case LONGWC: + return result.append(" *").toString(); + case WORD: + return result.append(" " + node.getContent()).toString(); + case NUMBER: + return result + " "; + default: + for (AGFNode gram : node.getChilds()) { + result.append(generateSentenceFromGrammar(gram)); + } + break; + } + + return result.toString(); + } + +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/PreDefinedEntityTypes.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/PreDefinedEntityTypes.java index f98acbf7c..57e96705f 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/PreDefinedEntityTypes.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/PreDefinedEntityTypes.java @@ -24,7 +24,9 @@ package de.unistuttgart.iaas.amyassist.amy.natlang; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Map.Entry; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFLexer; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFParser; @@ -42,9 +44,6 @@ public class PreDefinedEntityTypes { private static Map map; private static Map grammars; - private static final String[] ids = { "amyinteger", "amyhour", "amyminute", "amytime", "amydayofmonth", - "amydayofweek", "amymonth", "amydate", "amydatetime" }; - private PreDefinedEntityTypes() { // hide constructor } @@ -57,17 +56,18 @@ private PreDefinedEntityTypes() { public static Map getTypes() { if (grammars == null) { - grammars = new HashMap<>(); + grammars = new LinkedHashMap<>(); grammars.put("amyinteger", "$(0,1000000000, 1)"); - grammars.put("amyhour", "$(0,24,1)"); - grammars.put("amyminute", "$(0,60,1)"); + grammars.put("amyhour", "$(0,23,1)"); + grammars.put("amyminute", "$(0,59,1)"); grammars.put("amydayofmonth", "$(1,31,1)"); grammars.put("amydayofweek", "(monday|tuesday|wednesday|thursday|friday|saturday|sunday)"); grammars.put("amymonth", "(january|february|march|april|may|june|july|august|september|october|november|december|$(1,12,1))"); grammars.put("amytime", "(({amyhour} (x|oh) {amyminute}| (({amyhour}|quarter|half)" + " (past|to) {amyminute} )|{amyhour} [o clock])[am|pm]|now|no)"); - grammars.put("amydate", " ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{amyinteger}]|tomorrow|today)"); + grammars.put("amyyear", "$(1000,9999,1)"); + grammars.put("amydate", " ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{amyyear}]|tomorrow|today)"); grammars.put("amydatetime", "{amydate} at {amytime}"); } if (map == null) { @@ -84,10 +84,10 @@ public static Map getTypes() { * hashmap of grammars to generate */ private static void generateAGFNodes(Map grmrs) { - for (String s : ids) { - AGFLexer lex = new AGFLexer(grmrs.get(s)); + for (Entry entry : grmrs.entrySet()) { + AGFLexer lex = new AGFLexer(entry.getValue()); AGFParser parser = new AGFParser(lex, map); - map.put(s.toLowerCase(), parser.parseWholeExpression()); + map.put(entry.getKey().toLowerCase(), parser.parseWholeExpression()); } } } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/AGFLexer.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/AGFLexer.java index 09ff152ec..e9959a04d 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/AGFLexer.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/AGFLexer.java @@ -95,7 +95,9 @@ public AGFToken next() { case 32: return this.next(); default: - if (Character.isLetterOrDigit(c)) { + if (Character.getType(c) == Character.LOWERCASE_LETTER || + Character.getType(c) == Character.OTHER_LETTER || + Character.getType(c) == Character.DECIMAL_DIGIT_NUMBER) { currentWord.append(c); while (hasNext() && Character.isLetterOrDigit(this.mToLex.charAt(this.mIndex))) { currentWord.append(this.mToLex.charAt(this.mIndex)); diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/AGFNode.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/AGFNode.java index 8feced7f6..aa3fc0363 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/AGFNode.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/AGFNode.java @@ -180,7 +180,7 @@ public void deleteEntityContent() { * @return String to print */ public String printSelf() { - return printSelf("AGFNode", 0); + return printSelf(this.getClass().getSimpleName(), 0); } /** @@ -200,4 +200,24 @@ public int countLeafes() { return count; } + /** + * returns all contained child nodes + * + * @return List + */ + public List getChildWordNodes() { + List result = new ArrayList<>(); + + for (AGFNode node : this.childs) { + if (node.getType() == AGFNodeType.WORD) { + WordNode entity = (WordNode) node; + result.add(entity); + } + result.addAll(node.getChildWordNodes()); + } + + return result; + } + + } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/EndNode.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/EndNode.java new file mode 100644 index 000000000..ed413dfb8 --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/EndNode.java @@ -0,0 +1,69 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes; + +/** + * Helper class acting as a parent for all + * nodes that are end nodes - e.g. word nodes or number nodes + * @author Felix Burk + */ +public class EndNode extends AGFNode { + + /** + * @param content + */ + public EndNode(String content) { + super(content); + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode#getType() + */ + @Override + public AGFNodeType getType() { + //this is only a helper class and does not have an own agf node type + return null; + } + + /** + * special print self method, because this node my never have children + * + * @see de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode#printSelf(java.lang.String, int) + */ + @Override + public String printSelf(String name, int indent) { + StringBuilder b = new StringBuilder(); + b.append("+" + name); + b.append("\n"); + + for (int i = 0; i < indent; i++) { + b.append("|"); + b.append(" "); + } + b.append("|---" + " " + this.getContent()); + b.append("\n"); + return b.toString(); + } + +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/NumberNode.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/NumberNode.java index cc22a2174..c660e6d03 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/NumberNode.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/NumberNode.java @@ -28,7 +28,7 @@ * * @author Felix Burk */ -public class NumberNode extends AGFNode { +public class NumberNode extends EndNode { private int containedNumber; @@ -66,26 +66,6 @@ public AGFNodeType getType() { return AGFNodeType.NUMBER; } - /** - * special print self method, because this node my never have children - * - * @see de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode#printSelf(java.lang.String, int) - */ - @Override - public String printSelf(String name, int indent) { - StringBuilder b = new StringBuilder(); - b.append("+" + name); - b.append("\n"); - - for (int i = 0; i < indent; i++) { - b.append("|"); - b.append(" "); - } - b.append("|---" + " " + this.getContent()); - b.append("\n"); - return b.toString(); - } - /** * Get's {@link #containedNumber containedNumber} * diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/WordNode.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/WordNode.java index e01d0de26..6e86c2b9b 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/WordNode.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/agf/nodes/WordNode.java @@ -28,7 +28,7 @@ * * @author Felix Burk */ -public class WordNode extends AGFNode { +public class WordNode extends EndNode { /** * constructor @@ -50,24 +50,4 @@ public AGFNodeType getType() { return AGFNodeType.WORD; } - /** - * special print self method, because this node my never have children - * - * @see de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode#printSelf(java.lang.String, int) - */ - @Override - public String printSelf(String name, int indent) { - StringBuilder b = new StringBuilder(); - b.append("+" + name); - b.append("\n"); - - for (int i = 0; i < indent; i++) { - b.append("|"); - b.append(" "); - } - b.append("|---" + " " + this.getContent()); - b.append("\n"); - return b.toString(); - } - } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/ChooseLanguage.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/ChooseLanguage.java index 480ec82a3..f12954f84 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/ChooseLanguage.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/ChooseLanguage.java @@ -23,10 +23,7 @@ package de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics; -import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.EnglishContraction; -import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.EnglishDateTimeUtility; -import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.EnglishNumberConversion; -import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.EnglishStemmer; +import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.*; /** * @@ -37,6 +34,7 @@ public class ChooseLanguage { private final NumberConversion numberConversion; private final DateTimeUtility timeUtility; private final Contraction contraction; + private final SpecialCharacterConversion specialCharacterConversion; /** * constructor for choose language @@ -58,6 +56,7 @@ public ChooseLanguage(String language, boolean stemmerEnabled) { this.numberConversion = new EnglishNumberConversion(); this.timeUtility = new EnglishDateTimeUtility(); this.contraction = new EnglishContraction(); + this.specialCharacterConversion = new EnglishSpecialCharacterConversion(); break; } } @@ -97,4 +96,13 @@ public DateTimeUtility getTimeUtility() { public Contraction getContraction() { return this.contraction; } + + /** + * Get's {@link #specialCharacterConversion conversion} + * + * @return contraction + */ + public SpecialCharacterConversion getSpecialCharacterConversion() { + return this.specialCharacterConversion; + } } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/NumberConversion.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/NumberConversion.java index 155a63916..e7b714466 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/NumberConversion.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/NumberConversion.java @@ -25,7 +25,7 @@ import java.util.Map; -import de.unistuttgart.iaas.amyassist.amy.natlang.nl.WordToken; +import de.unistuttgart.iaas.amyassist.amy.natlang.nl.EndToken; /** * This interface provide methods for the number conversion @@ -42,7 +42,7 @@ public interface NumberConversion { * the sublist containing the list of word representations * @return the calculated number */ - int calcNumber(Iterable subList); + int calcNumber(Iterable subList); /** * Get's {@link #wordToNumber wordToNumber} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/SpecialCharacterConversion.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/SpecialCharacterConversion.java new file mode 100644 index 000000000..87f77cfe2 --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/SpecialCharacterConversion.java @@ -0,0 +1,41 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics; + +/** + * Utility interface to convert special characters + * + * @author Felix Burk + */ +public interface SpecialCharacterConversion { + + /** + * formats special characters to their language counterpars + * e.g. % to percent + * @param toFormat input to format + * @return formatted string + */ + String format(String toFormat); + +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishNumberConversion.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishNumberConversion.java index 37f87b007..6244d7c94 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishNumberConversion.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishNumberConversion.java @@ -33,7 +33,7 @@ import java.util.stream.Stream; import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.NumberConversion; -import de.unistuttgart.iaas.amyassist.amy.natlang.nl.WordToken; +import de.unistuttgart.iaas.amyassist.amy.natlang.nl.EndToken; /** * This class is loading language specific numbers @@ -65,10 +65,10 @@ public EnglishNumberConversion() { * @return the calculated number */ @Override - public int calcNumber(Iterable subList) { + public int calcNumber(Iterable subList) { int finalNumber = 0; int partialNumber = 0; - for (WordToken t : subList) { + for (EndToken t : subList) { if (this.wordToNumber.get(t.getContent()) >= 1000) { if (partialNumber == 0) partialNumber = 1; diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishSpecialCharacterConversion.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishSpecialCharacterConversion.java new file mode 100644 index 000000000..4c8d18f77 --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/languagespecifics/en/EnglishSpecialCharacterConversion.java @@ -0,0 +1,69 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.SpecialCharacterConversion; + +/** + * Special Character conversion for the english language + * + * @author Felix Burk + */ +public class EnglishSpecialCharacterConversion implements SpecialCharacterConversion { + + private Map conversion; + + public EnglishSpecialCharacterConversion() { + this.conversion = new HashMap<>(); + + this.conversion.put("%", "percent"); + this.conversion.put("$", "dollar"); + this.conversion.put("€", "euro"); + this.conversion.put("¢", "cent"); + this.conversion.put("£", "pound"); + this.conversion.put("¥", "yen"); + this.conversion.put("°", "degree"); + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.SpecialCharacterConversion#format(java.lang.String) + */ + @Override + public String format(String toFormat) { + String result = toFormat; + //this is kinda ugly buuut regex and special characters is pretty bad + for(Entry e : this.conversion.entrySet()) { + if(result.contains(e.getKey())) { + result = toFormat.replaceAll(e.getKey(), e.getValue()); + } + } + + return result; + } + +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndToken.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndToken.java new file mode 100644 index 000000000..3789642fb --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndToken.java @@ -0,0 +1,79 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang.nl; + +/** + * Word Token class used in the Lexer and parser + * + * @author Felix Burk + */ +public class EndToken { + + private final String content; + + private EndTokenType type; + + /** + * constructor for word token + * + * @param content + * the content + */ + public EndToken(String content) { + this.content = content; + } + + /** + * getter for string content + * + * @return the content + */ + public String getContent() { + return this.content; + } + + /** + * setter for type + * + * @param type + * the type to set + */ + public void setType(EndTokenType type) { + this.type = type; + } + + /** + * returns the type + * + * @return the type + */ + public EndTokenType getType() { + return this.type; + } + + @Override + public String toString() { + return this.content; + } +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndTokenType.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndTokenType.java new file mode 100644 index 000000000..b0c955bc4 --- /dev/null +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/EndTokenType.java @@ -0,0 +1,43 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.natlang.nl; + +/** + * types of word tokens + * + * @author Felix Burk + */ +public enum EndTokenType { + + /** + * just some word + */ + WORD, + + /** + * some number, written or as digit + */ + NUMBER; + +} diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/INLParser.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/INLParser.java index c4adf14d6..802043934 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/INLParser.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/INLParser.java @@ -42,7 +42,7 @@ public interface INLParser { * * @return the matching AGFNode */ - public int matchingNodeIndex(List nl); + public int matchingNodeIndex(List nl); /** * returns matching AGFNode @@ -52,6 +52,6 @@ public interface INLParser { * * @return the matching AGFNode */ - public AGFNode matchingNode(List nl); + public AGFNode matchingNode(List nl); } diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLLexer.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLLexer.java index 60216e022..b6ee6717c 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLLexer.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLLexer.java @@ -23,11 +23,7 @@ package de.unistuttgart.iaas.amyassist.amy.natlang.nl; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; +import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,7 +45,7 @@ public class NLLexer { /** * contains regex with the corresponding WordTokenType */ - private final Map regexTokenType = new HashMap<>(); + private final Map regexTokenType = new LinkedHashMap<>(); private ChooseLanguage language; @@ -58,7 +54,7 @@ public class NLLexer { /** * this class handles natural language input of any type * - * @param iNumberConversion + * @param language * language specific details * */ @@ -68,13 +64,12 @@ public NLLexer(ChooseLanguage language) { if (!this.numberConversion.getWordToNumber().isEmpty()) { String regex = "((\\b" + String.join("\\b|\\b", this.numberConversion.getWordToNumber().keySet()) + "\\b)\\s{0,1})+"; - this.regexTokenType.put(regex, WordTokenType.NUMBER); + this.regexTokenType.put(regex, EndTokenType.NUMBER); } else { this.logger.error("problem with numbers file, written numbers will not be recognized"); } - - this.regexTokenType.put("[a-zA-Z]+", WordTokenType.WORD); - this.regexTokenType.put("[0-9]+", WordTokenType.NUMBER); + this.regexTokenType.put("[0-9]+", EndTokenType.NUMBER); + this.regexTokenType.put("(\\p{Lo}|\\p{L})+", EndTokenType.WORD); } /** @@ -84,16 +79,14 @@ public NLLexer(ChooseLanguage language) { * the string to lex * @return returns processed list of WordTokens */ - public List tokenize(String nlInput) { - List list = new LinkedList<>(); + public List tokenize(String nlInput) { + List list = new LinkedList<>(); String toLex = nlInput.toLowerCase().trim(); toLex = this.language.getTimeUtility().formatTime(toLex); toLex = this.language.getTimeUtility().formatDate(toLex); toLex = this.language.getContraction().disassemblingContraction(toLex); - // replace all punctuation - toLex = toLex.replaceAll("\\,|\\.|\\:|\\;|\\?|\\!", ""); - toLex = toLex.replaceAll("-", " "); - toLex = toLex.trim(); + toLex = this.language.getSpecialCharacterConversion().format(toLex); + StringBuilder currentWord = new StringBuilder(); if (!toLex.isEmpty()) { for (int mIndex = 0; mIndex < toLex.length(); mIndex++) { @@ -101,21 +94,26 @@ public List tokenize(String nlInput) { switch (Character.getType(c)) { case Character.LOWERCASE_LETTER: //$FALL-THROUGH$ + case Character.OTHER_LETTER: + //$FALL-THROUGH$ case Character.DECIMAL_DIGIT_NUMBER: currentWord.append(c); break; // handles single whitespace characters but not newline, tab or carriage return case Character.SPACE_SEPARATOR: + //$FALL-THROUGH$ + default: if (currentWord.length() != 0) { - list.add(parse(new WordToken(currentWord.toString()))); + list.add(parse(new EndToken(currentWord.toString()))); currentWord = new StringBuilder(); + break; } - break; - default: - throw new NLLexerException("character not recognized " + c + " stopping"); + continue; } } - list.add(parse(new WordToken(currentWord.toString()))); + if(currentWord.length() != 0) { + list.add(parse(new EndToken(currentWord.toString()))); + } return concatNumbers(list); } return new ArrayList<>(); @@ -129,11 +127,11 @@ public List tokenize(String nlInput) { * of all tokens containing potential written numbers that have to be merged * @return final list containing the correct numbers */ - private List concatNumbers(List list) { - List result = new ArrayList<>(); - List numbers = new ArrayList<>(); - for (WordToken wordToken : list) { - if (wordToken.getType() == WordTokenType.NUMBER && wordToken.getContent().matches("[a-zA-Z]+")) { + private List concatNumbers(List list) { + List result = new ArrayList<>(); + List numbers = new ArrayList<>(); + for (EndToken wordToken : list) { + if (wordToken.getType() == EndTokenType.NUMBER && wordToken.getContent().matches("[a-zA-Z]+")) { numbers.add(wordToken); } else { if (!numbers.isEmpty()) { @@ -149,10 +147,10 @@ private List concatNumbers(List list) { return result; } - private WordToken fromNumbers(List numbers) { + private EndToken fromNumbers(List numbers) { int finalNumber = this.numberConversion.calcNumber(numbers); - WordToken t = new WordToken(String.valueOf(finalNumber)); - t.setType(WordTokenType.NUMBER); + EndToken t = new EndToken(String.valueOf(finalNumber)); + t.setType(EndTokenType.NUMBER); return t; } @@ -163,8 +161,8 @@ private WordToken fromNumbers(List numbers) { * the WordToken * @return the parsed WordToken */ - private WordToken parse(WordToken next) { - for (Map.Entry entry : this.regexTokenType.entrySet()) { + private EndToken parse(EndToken next) { + for (Map.Entry entry : this.regexTokenType.entrySet()) { if (next.getContent().matches(entry.getKey())) { next.setType(entry.getValue()); return next; diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLParser.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLParser.java index 209fb49b7..be6d169aa 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLParser.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/nl/NLParser.java @@ -23,8 +23,9 @@ package de.unistuttgart.iaas.amyassist.amy.natlang.nl; -import java.util.Collections; -import java.util.List; +import java.util.*; + +import org.apache.commons.lang3.StringUtils; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFParseException; import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.*; @@ -46,11 +47,31 @@ public class NLParser implements INLParser { /** * list of read tokens */ - private List mRead; + private List mRead; + /** + * current index of read tokens + */ private int currentIndex; + /** + * a stemmer instance - this handles matches like + * likes - like + */ private Stemmer stemmer; + + /** + * a deque containing helper ORGroups to indicate + * the + wildcard when to stop matching + */ + private Deque shortWCStopper; + + /** + * contains all matched strings + * meaning the content of the node that matched + * and if a wildcard matched to matching string + */ + private List matchedStrings; /** * @@ -65,9 +86,11 @@ public NLParser(List grammars, Stemmer stemmer) { } @Override - public AGFNode matchingNode(List nl) { + public AGFNode matchingNode(List nl) { this.mRead = nl; for (AGFNode agf : this.grammars) { + this.matchedStrings = new ArrayList<>(); + this.shortWCStopper = generateStopperDeque(agf); agf.deleteEntityContent(); AGFNode nodeSorted = sortChildsOfOrAndOp(agf); this.currentIndex = 0; @@ -78,6 +101,91 @@ public AGFNode matchingNode(List nl) { throw new NLParserException("could not find matching grammar for tokens" + nl); } + /** + * use this method to generate the stack + * see {@link #generateStopperDeque(AGFNode, Deque) generateStopperStack}} + * + * @param agf node to generate stack from + * @return the generated deque + */ + public Deque generateStopperDeque(AGFNode agf) { + Deque stack = new ArrayDeque<>(); + this.possibleStoppers = new ArrayList<>(); + generateStopperDeque(agf, stack); + if(!this.possibleStoppers.isEmpty()) { + stack.push(generateHelperORGroup(this.possibleStoppers)); + this.foundShortWC = false; + } + return stack; + } + + /** + * generates a helper ORGroup. + * This ORGroup is used in {{@link #generateStopperDeque(AGFNode, Deque)} + * the children in the group are possible stoppers for short wildcards. + * for example: + * test + [test2] [test3] test4 + * then the helperORGroup used as a stopper would look like this + * (test2|test3|test4) + * because the optional groups might not be used + * + * @param children to add to group + * @return ORGroup inside an AGFNode to maintain the traceback ability + */ + private AGFNode generateHelperORGroup(List children) { + AGFNode parent = new AGFNode(""); + ORGroupNode orGroup = new ORGroupNode(""); + for(AGFNode node : children) { + orGroup.addChild(node); + } + parent.addChild(orGroup); + return parent; + } + + /** + * indicated if a + wildcard has been found + */ + private boolean foundShortWC = false; + /** + * a list of all possible stoppers for + wildcards + * that will be combined in a generated Helper ORGroup + */ + private List possibleStoppers; + /** + * generates a deque which tells a short wildcard when to stop + * e.g. in the grammar "test + testi + testo" + * the stack would contain "testo", "testi". The first + wildcard then knows + * to stop at "testi", because its on last in the queue + * "testi" gets removed after + matched. + * Then the next + knows to stop at testo because its last in the queue. + * + * @param agf node to generate stack from + * @param stack the stack + */ + private void generateStopperDeque(AGFNode agf, Deque stack) { + for(AGFNode node : agf.getChilds()) { + if(node.getType() == AGFNodeType.SHORTWC && !this.foundShortWC) { + this.foundShortWC = true; + }else if(this.foundShortWC) { + //optinal group hack + if(node.getType() == AGFNodeType.OPG) { + for(AGFNode opChild : node.getChilds()) { + this.possibleStoppers.add(opChild); + } + }else { + this.possibleStoppers.add(node); + stack.push(generateHelperORGroup(this.possibleStoppers)); + this.possibleStoppers = new ArrayList<>(); + + this.foundShortWC = false; + } + + }else { + generateStopperDeque(node, stack); + } + } + } + /** * sorts childs of or groups and optional groups by size size meaning number of words inside the sentences seperated * by '|' @@ -105,17 +213,13 @@ public AGFNode sortChildsOfOrAndOp(AGFNode node) { } @Override - public int matchingNodeIndex(List nl) { + public int matchingNodeIndex(List nl) { return this.grammars.indexOf(matchingNode(nl)); } /** - * ugly internal state variable to show how many skips are allowed currently - */ - int wcSkips = 0; - - /** - * another ugly internal state for wildcards if this is true everything will be skipped and return true + * another ugly internal state for infinite * wildcards + * if this is true everything will be skipped and return true */ boolean wildcardSkip = false; @@ -133,6 +237,7 @@ private boolean checkNode(AGFNode agf) { for (AGFNode node : agf.getChilds()) { if (!checkNode(node)) { this.currentIndex = traceBack; + this.matchedStrings = this.matchedStrings.subList(0, this.currentIndex); agf.deleteEntityContent(); return false; } @@ -161,14 +266,9 @@ private boolean checkNode(AGFNode agf) { } break; case SHORTWC: - this.wcSkips = ((ShortWNode) agf).getMaxWordLength(); - break; + return matchShortWC(agf); case LONGWC: - this.wildcardSkip = true; - for (int i = this.currentIndex; i < this.mRead.size(); i++) { - consume(); - } - break; + return matchLongWC(); case WORD: return match(agf); case NUMBER: @@ -183,28 +283,60 @@ private boolean checkNode(AGFNode agf) { } /** - * checks if a number is at the current index and if the number matches the conditions of the NumberNode e.g. is in - * correct range and stepsize - * - * @param agf - * to match - * @return true if the node matches + * matches a greedy long wildcard * with infinite input + * this consumes the whole input left! only use * at the end of a grammar + * + * @return if it matches */ - private boolean matchNumber(AGFNode agf) { - WordToken token = lookAhead(0); - - if (token == null || token.getContent() == null) { + private boolean matchLongWC() { + this.wildcardSkip = true; + //quick hack - match at least one character + if(this.currentIndex == this.mRead.size()) { return false; } - try { - NumberNode numberNode = (NumberNode) agf; - numberNode.setContainedNumber(token.getContent().trim()); - consume(); - } catch (ClassCastException e) { - throw new NLParserException("Node Type Number was no NumberNode " + e); - } catch (NumberFormatException e) { + for (int i = this.currentIndex; i < this.mRead.size(); i++) { + EndToken token = lookAhead(0); + if(token != null) { + this.matchedStrings.add(token.getContent()); + consume(); + }else { + break; + } + } + return true; + } + + /** + * matches a short non greedy wildcard with fixed skip size + * this uses the internal deque to check the end of the wildcards + * + * @param agf to check + * @return if the short wildcard matched + */ + private boolean matchShortWC(AGFNode agf) { + AGFNode endNode = this.shortWCStopper.peekLast(); + if(endNode == null) { return false; } + + ShortWNode wc = (ShortWNode) agf; + int i; + int skips = 0; + int index = this.currentIndex; + for(i=0; (i < wc.getMaxWordLength() && i < this.mRead.size()-index); i++) { + EndToken token = this.lookAhead(0); + if(checkNode(endNode)) { + //undo consume from checkNode + this.currentIndex = index+skips; + this.shortWCStopper.pollLast(); + break; + }else if(token != null) { + //skip current word because it did not match the end criteria + this.matchedStrings.add(token.getContent()); + skips++; + consume(); + } + } return true; } @@ -225,12 +357,11 @@ private boolean fillEntity(AGFNode agf) { try { EntityNode entity = (EntityNode) agf; - StringBuilder b = new StringBuilder(); - for (int i = startIndex; i <= endIndex - 1; i++) { - b.append(this.mRead.get(i) + " "); + if (matched) { + String content = StringUtils.join(this.matchedStrings.subList(startIndex, endIndex), " "); + entity.setUserProvidedContent(content); + } - if (matched) - entity.setUserProvidedContent(b.toString().trim()); return matched; } catch (ClassCastException e) { throw new NLParserException("Node Type Entity was no EntityNode " + e); @@ -245,35 +376,23 @@ private boolean fillEntity(AGFNode agf) { * @return if it matched */ private boolean match(AGFNode toMatch) { - WordToken token = lookAhead(0); + EndToken token = lookAhead(0); if (compareWord(toMatch, token)) { consume(); return true; } - - // greedy match words with fixed size of skips for wildcards - if (this.wcSkips != 0) { - int lookaheadSize = 0; - for (int i = 1; i <= this.wcSkips; i++) { - WordToken temp = lookAhead(i); - if (compareWord(toMatch, temp)) { - lookaheadSize = i; - break; - } - } - for (int i = 0; i < lookaheadSize + 1; i++) { - consume(); - } - if (lookaheadSize != 0) { - return true; - } - } - this.wcSkips = 0; return false; } - private boolean compareWord(AGFNode toMatch, WordToken token) { + /** + * compare a AGFNode to a word endtoken + * + * @param toMatch node to compare to + * @param token to compare to + * @return if a match is possible + */ + private boolean compareWord(AGFNode toMatch, EndToken token) { if(toMatch == null || toMatch.getContent() == null || token == null || token.getContent() == null) return false; @@ -285,17 +404,60 @@ private boolean compareWord(AGFNode toMatch, WordToken token) { tokenContent = this.stemmer.stem(token.getContent()); } if (nodeContent.equals(tokenContent)) { + this.matchedStrings.add(toMatch.getContent()); + return true; + } + if(nodeContent.length() > 3 && !CompareWords.isDistanceBigger(nodeContent, tokenContent, 1)) { + this.matchedStrings.add(toMatch.getContent()); return true; } - return !CompareWords.isDistanceBigger(nodeContent, tokenContent, 1); + return false; + } + + /** + * checks if a number is at the current index and if the number matches the conditions of the NumberNode e.g. is in + * correct range and stepsize + * + * @param agf + * to match + * @return true if the node matches + */ + private boolean matchNumber(AGFNode agf) { + EndToken token = lookAhead(0); + return matchNumber(agf, token); + } + + /** + * checks if a endtoken number matches an AGFNode + * + * @param agf to compare to + * @param token to compare to + * @return if a match is possible + */ + private boolean matchNumber(AGFNode agf, EndToken token) { + if (token == null || token.getContent() == null) { + return false; + } + try { + NumberNode numberNode = (NumberNode) agf; + numberNode.setContainedNumber(token.getContent().trim()); + consume(); + } catch (ClassCastException e) { + throw new NLParserException("Node Type Number was no NumberNode " + e); + } catch (NumberFormatException e) { + return false; + } + this.matchedStrings.add(token.getContent()); + return true; } + /** * consume a token * * @return consumed token */ - private WordToken consume() { + private EndToken consume() { if (this.mRead.size() > this.currentIndex) { return this.mRead.get(this.currentIndex++); } @@ -309,7 +471,7 @@ private WordToken consume() { * needed * @return token at distance */ - private WordToken lookAhead(int distance) { + private EndToken lookAhead(int distance) { int index = this.currentIndex + distance; if (this.mRead.size() > index) { // Get the queued token. diff --git a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/userinteraction/UserIntent.java b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/userinteraction/UserIntent.java index ec9a4b954..c01a1da3f 100644 --- a/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/userinteraction/UserIntent.java +++ b/natlang/src/main/java/de/unistuttgart/iaas/amyassist/amy/natlang/userinteraction/UserIntent.java @@ -24,14 +24,12 @@ package de.unistuttgart.iaas.amyassist.amy.natlang.userinteraction; import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import javax.annotation.Nonnull; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -67,7 +65,7 @@ public class UserIntent { /** * internal list of all entities */ - private Map entityList = new HashMap<>(); + private Map entityList = new LinkedHashMap<>(); /** * Represents an intent of a user @@ -100,12 +98,9 @@ private void registerEntities() { } - Map idToPrompt = new HashMap<>(); for (XMLPrompt xmlPrompt : this.aimIntent.getPrompts()) { - idToPrompt.put(xmlPrompt.getEntityTemplateId(), - new Prompt(parseStringToAGF(xmlPrompt.getGram()), xmlPrompt.getText())); Entity e = this.entityList.get(xmlPrompt.getEntityTemplateId()); - e.setPrompt(idToPrompt.get(xmlPrompt.getEntityTemplateId())); + e.setPrompt(new Prompt(parseStringToAGF(xmlPrompt.getGram()), xmlPrompt.getText())); e.setMethod(NLIAnnotationReader.getValidEnityProviderMethod(this.partialNLIClass, e.getEntityId())); this.entityList.replace(xmlPrompt.getEntityTemplateId(), e); } @@ -138,7 +133,7 @@ private AGFNode parseStringToAGF(String toParse) { * map of all entities with the id as key * @return the result String from calling the command */ - public String call(Object instance, Map map) { + public Response call(Object instance, Map map) { Object[] params = { map }; return NLIAnnotationReader.callNLIMethod(this.method, instance, params); } diff --git a/natlang/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/natlang/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services index 360f13ce6..99a0ad121 100644 --- a/natlang/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services +++ b/natlang/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -1,3 +1,4 @@ de.unistuttgart.iaas.amyassist.amy.natlang.NLProcessingManagerImpl de.unistuttgart.iaas.amyassist.amy.natlang.userinteraction.LoadAIMService -de.unistuttgart.iaas.amyassist.amy.natlang.DialogHandlerImpl \ No newline at end of file +de.unistuttgart.iaas.amyassist.amy.natlang.DialogHandlerImpl +de.unistuttgart.iaas.amyassist.amy.natlang.NatlangInformationImpl \ No newline at end of file diff --git a/natlang/src/main/resources/META-INF/natlang.config.properties b/natlang/src/main/resources/META-INF/natlang.config.properties new file mode 100644 index 000000000..8c32d8913 --- /dev/null +++ b/natlang/src/main/resources/META-INF/natlang.config.properties @@ -0,0 +1,2 @@ +enableStemmer=true +chooseLanguage=en \ No newline at end of file diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandlerImplTest.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandlerImplTest.java index ba627497c..f1b2a617e 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandlerImplTest.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/DialogHandlerImplTest.java @@ -66,7 +66,7 @@ public class DialogHandlerImplTest { private UUID uuid; @Mock - private Consumer consumer; + private Consumer consumer; private ServiceLocator serviceLocator; diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NLProcessingManagerTest.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NLProcessingManagerTest.java index 4c7e7d35a..e91b90c1d 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NLProcessingManagerTest.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NLProcessingManagerTest.java @@ -93,8 +93,8 @@ public void test() { assertEquals(true, this.quitIntent.contains(this.check)); } - private void consumerMethodForDialog(String s) { - this.check = s; + private void consumerMethodForDialog(Response response) { + this.check = response.getText(); } } diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformationTest.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformationTest.java new file mode 100644 index 000000000..c0de7f741 --- /dev/null +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/NatlangInformationTest.java @@ -0,0 +1,105 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.core.natlang; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.natlang.NLProcessingManager; +import de.unistuttgart.iaas.amyassist.amy.natlang.NatlangInformationImpl; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFLexer; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.AGFParser; +import de.unistuttgart.iaas.amyassist.amy.natlang.agf.nodes.AGFNode; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * Test for NatlangInformation Service + * @author Felix Burk + */ +@ExtendWith({ MockitoExtension.class, FrameworkExtension.class }) +public class NatlangInformationTest { + + @Reference + private TestFramework testFramework; + + /** + * mock service + */ + private NLProcessingManager manager; + /** + * service to test + */ + private NatlangInformation info; + + /** + * initializer + */ + @BeforeEach + void init() { + this.manager = this.testFramework.mockService(NLProcessingManager.class); + this.info = this.testFramework.setServiceUnderTest(NatlangInformationImpl.class); + String gram = "test [some] (weird | strange [test] grammar)"; + AGFLexer lex = new AGFLexer(gram); + AGFParser parser = new AGFParser(lex); + List list = new ArrayList<>(); + list.add(parser.parseWholeExpression()); + + when(this.manager.getPossibleGrammars()).thenReturn(list); + } + + /** + * tests generating sample sentences from a grammar + */ + @Test + public void generateSampleSentenceTest() { + List list = this.info.getAnySampleSentences(1); + String result = list.get(0); + assertEquals(true, result.contains("test")); + assertEquals(true, (result.contains("weird") || result.contains("strange") && result.contains("grammar"))); + } + + /** + * tests generating sample sentences of a grammar containing a keyword + */ + @Test + public void generateSentenceWithKeyword() { + List list = this.info.getSampleSentencesFromKeyword("test", 1); + String result = list.get(0); + assertEquals(true, result.contains("test")); + assertEquals(true, (result.contains("weird") || result.contains("strange") && result.contains("grammar"))); + + List list2 = this.info.getSampleSentencesFromKeyword("xx", 1); + assertEquals(true, list2.isEmpty()); + } +} diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/PluginMockFactory.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/PluginMockFactory.java index 3634e9efa..d1f7ab2d8 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/PluginMockFactory.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/PluginMockFactory.java @@ -1,3 +1,26 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + package de.unistuttgart.iaas.amyassist.amy.core.natlang; import java.nio.file.Path; diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/TestNLIAnnotationReader.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/TestNLIAnnotationReader.java index c33e9fe0e..562224604 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/TestNLIAnnotationReader.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/TestNLIAnnotationReader.java @@ -83,7 +83,8 @@ public String count(String s) { public void testIllegalReturnType() { String message = assertThrows(IllegalArgumentException.class, () -> NLIAnnotationReader.getValidIntentMethods(BrokenReturnType.class)).getMessage(); - assertThat(message, equalTo("The returntype of a method annotated with @Intent should be String.")); + assertThat(message, equalTo("The returntype of a method annotated with @Intent should be either " + + "String or Response.")); } @SpeechCommand diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLLexerTest.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLLexerTest.java index 7a67cf255..4333bacb4 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLLexerTest.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLLexerTest.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @@ -44,8 +45,8 @@ import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.ChooseLanguage; import de.unistuttgart.iaas.amyassist.amy.natlang.nl.NLLexer; import de.unistuttgart.iaas.amyassist.amy.natlang.nl.NLLexerException; -import de.unistuttgart.iaas.amyassist.amy.natlang.nl.WordToken; -import de.unistuttgart.iaas.amyassist.amy.natlang.nl.WordTokenType; +import de.unistuttgart.iaas.amyassist.amy.natlang.nl.EndToken; +import de.unistuttgart.iaas.amyassist.amy.natlang.nl.EndTokenType; /** * Test for NL Lexer @@ -71,8 +72,9 @@ public void setup() { */ public static Stream badCharacters() { // every ascii character except numbers of letters - return IntStream.range(0, 128).mapToObj(i -> (char) i).filter(c -> (0 > c && c > 33) || (34 < c && c != 37 && !(43 < c && c < 47) && c < 48) - || (59 < c && c < 65 && c != 63) || (90 < c && c < 97) || 122 < c); + return IntStream.range(0, 128).mapToObj(i -> (char) i) + .filter(c -> ((0 > c && c > 33) || (34 < c && !(43 < c && c < 47) && c != 36 && c != 37 && c < 48) + || (59 < c && c < 65 && c != 63) || (90 < c && c < 97) || 122 < c)); } /** @@ -85,7 +87,9 @@ public static Stream badCharacters() { @MethodSource("badCharacters") public void testBadChars(Character badCharacter) { NLLexer lexer = new NLLexer(this.lang); - assertThrows(NLLexerException.class, () -> lexer.tokenize(badCharacter.toString())); + String test = ""; + lexer.tokenize(badCharacter.toString()).stream().map(s -> s.getContent()).collect(Collectors.joining("")); + assertEquals(true, test.equals("")); } /** @@ -107,8 +111,8 @@ public static Stream> testWords() { @MethodSource("testWords") public void checkInput(List input) { NLLexer lexer = new NLLexer(this.lang); - List tokenize = lexer.tokenize(String.join(" ", input)); - assertThat(Lists.transform(tokenize, WordToken::getContent), is(input)); + List tokenize = lexer.tokenize(String.join(" ", input)); + assertThat(Lists.transform(tokenize, EndToken::getContent), is(input)); } /** @@ -117,10 +121,20 @@ public void checkInput(List input) { @Test public void testTypes() { NLLexer lex = new NLLexer(this.lang); - List tokenize = lex.tokenize("wajjo 9 0 oh 99 oh one"); - assertThat(Lists.transform(tokenize, WordToken::getType), - contains(WordTokenType.WORD, WordTokenType.NUMBER, WordTokenType.NUMBER, WordTokenType.WORD, - WordTokenType.NUMBER, WordTokenType.WORD, WordTokenType.NUMBER)); + List tokenize = lex.tokenize("wajjo 9 0 oh 99 oh one"); + assertThat(Lists.transform(tokenize, EndToken::getType), + contains(EndTokenType.WORD, EndTokenType.NUMBER, EndTokenType.NUMBER, EndTokenType.WORD, + EndTokenType.NUMBER, EndTokenType.WORD, EndTokenType.NUMBER)); + } + + @ParameterizedTest + @MethodSource("badCharacters") + public void testRemoveBadCharacters(Character badCharacter) { + NLLexer lex = new NLLexer(this.lang); + List tokenize = lex.tokenize("wajjo" + badCharacter + " 9 0 oh 99 oh one"); + assertThat(Lists.transform(tokenize, EndToken::getType), + contains(EndTokenType.WORD, EndTokenType.NUMBER, EndTokenType.NUMBER, EndTokenType.WORD, + EndTokenType.NUMBER, EndTokenType.WORD, EndTokenType.NUMBER)); } /** @@ -131,10 +145,10 @@ public void testTypes() { @Test public void testConcatNumbers() { NLLexer lex = new NLLexer(this.lang); - List tokenize = lex.tokenize("test one hundred twenty two test"); + List tokenize = lex.tokenize("test one hundred twenty two test"); - assertThat(Lists.transform(tokenize, WordToken::getType), - contains(WordTokenType.WORD, WordTokenType.NUMBER, WordTokenType.WORD)); + assertThat(Lists.transform(tokenize, EndToken::getType), + contains(EndTokenType.WORD, EndTokenType.NUMBER, EndTokenType.WORD)); assertThat(new Boolean(true), is(tokenize.get(1).getContent().equals("122"))); } @@ -185,7 +199,7 @@ public static Stream> numberStringToInt() { @MethodSource("numberStringToInt") public void testNumberConversion(Pair pair) { NLLexer lex = new NLLexer(this.lang); - List numbers = lex.tokenize(pair.getLeft()); + List numbers = lex.tokenize(pair.getLeft()); assertThat(new Boolean(true), is(numbers.get(0).getContent().equals((String.valueOf(pair.getRight()))))); } diff --git a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLParserTest.java b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLParserTest.java index 4353f581b..1f938b4cc 100644 --- a/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLParserTest.java +++ b/natlang/src/test/java/de/unistuttgart/iaas/amyassist/amy/core/natlang/nl/NLParserTest.java @@ -31,6 +31,7 @@ import java.io.InputStreamReader; import java.io.StringReader; import java.util.ArrayList; +import java.util.Deque; import java.util.List; import java.util.stream.Collectors; @@ -49,7 +50,7 @@ import de.unistuttgart.iaas.amyassist.amy.natlang.languagespecifics.en.EnglishNumberConversion; import de.unistuttgart.iaas.amyassist.amy.natlang.nl.NLLexer; import de.unistuttgart.iaas.amyassist.amy.natlang.nl.NLParser; -import de.unistuttgart.iaas.amyassist.amy.natlang.nl.WordToken; +import de.unistuttgart.iaas.amyassist.amy.natlang.nl.EndToken; import de.unistuttgart.iaas.amyassist.amy.natlang.userinteraction.UserIntent; /** @@ -74,11 +75,13 @@ public void setup() throws JAXBException, FileNotFoundException { UserIntent int0 = new UserIntent(this.getClass().getMethods()[0], aimmodel.getIntents().get(0)); UserIntent int1 = new UserIntent(this.getClass().getMethods()[0], aimmodel.getIntents().get(1)); UserIntent int2 = new UserIntent(this.getClass().getMethods()[0], aimmodel.getIntents().get(2)); + UserIntent int3 = new UserIntent(this.getClass().getMethods()[0], aimmodel.getIntents().get(3)); this.intents = new ArrayList<>(); this.intents.add(int0); this.intents.add(int1); this.intents.add(int2); + this.intents.add(int3); } @@ -86,7 +89,7 @@ public void setup() throws JAXBException, FileNotFoundException { @Test public void test() { NLLexer lex = new NLLexer(new ChooseLanguage("en", false)); - List tokens = lex.tokenize("greet me"); + List tokens = lex.tokenize("greet me"); List nodes = new ArrayList<>(); nodes.add(this.intents.get(0).getGrammar()); NLParser parser = new NLParser(nodes, null); @@ -97,7 +100,7 @@ public void test() { @Test public void finished() { NLLexer lex = new NLLexer(new ChooseLanguage("en", false)); - List tokens = lex.tokenize("greet me with good morning test ten oh twenty"); + List tokens = lex.tokenize("greet me with good morning test ten oh twenty"); List nodes = new ArrayList<>(); nodes.add(this.intents.get(0).getGrammar()); NLParser parser = new NLParser(nodes, null); @@ -106,10 +109,35 @@ public void finished() { assertEquals(getEntityNodeContent(parser.matchingNode(tokens)), "good morning10 oh 20"); } + /** + * tests stop conditions in + wildcards + */ + @Test + public void entityStopperGrammar() { + NLLexer lex = new NLLexer(new ChooseLanguage("en", false)); + List tokens = lex.tokenize("best transport from stuttgart test to berlin x test"); + List nodes = new ArrayList<>(); + nodes.add(this.intents.get(3).getGrammar()); + NLParser parser = new NLParser(nodes, null); + + Deque queue = parser.generateStopperDeque(nodes.get(0)); + AGFNode queueLast = queue.pollLast(); + assertEquals(AGFNodeType.AGF, queueLast.getType()); + AGFNode orGroup = queueLast.getChilds().get(0); + assertEquals(AGFNodeType.ORG, orGroup.getType()); + assertEquals(1, orGroup.getChilds().size()); + + AGFNode queueSecond = queue.pollLast(); + assertEquals(AGFNodeType.AGF, queueSecond.getType()); + AGFNode orGroupSecond = queueSecond.getChilds().get(0); + assertEquals(AGFNodeType.ORG, orGroupSecond.getType()); + assertEquals(2, orGroupSecond.getChilds().size()); + } + @Test public void shortWildcardTest() { NLLexer lex = new NLLexer(new ChooseLanguage("en", false)); - List tokens = lex.tokenize("test the four sign long wildcard here"); + List tokens = lex.tokenize("test the four sign wildcard long here"); List nodes = new ArrayList<>(); nodes.add(this.intents.get(1).getGrammar()); NLParser parser = new NLParser(nodes, null); @@ -126,7 +154,7 @@ public void longWildcardTest() { } b.append(" long"); NLLexer lex = new NLLexer(new ChooseLanguage("en", false)); - List tokens = lex.tokenize(b.toString()); + List tokens = lex.tokenize(b.toString()); List nodes = new ArrayList<>(); nodes.add(this.intents.get(2).getGrammar()); NLParser parser = new NLParser(nodes, null); diff --git a/natlang/src/test/resources/de/unistuttgart/iaas/amyassist/amy/core/natlang/userinteraction/testXMLUserInteraction.aim.xml b/natlang/src/test/resources/de/unistuttgart/iaas/amyassist/amy/core/natlang/userinteraction/testXMLUserInteraction.aim.xml index 787131126..9b8aa51fb 100644 --- a/natlang/src/test/resources/de/unistuttgart/iaas/amyassist/amy/core/natlang/userinteraction/testXMLUserInteraction.aim.xml +++ b/natlang/src/test/resources/de/unistuttgart/iaas/amyassist/amy/core/natlang/userinteraction/testXMLUserInteraction.aim.xml @@ -25,4 +25,26 @@ ref="de.unistuttgart.iaas.amyassist.amy.plugin.example.HelloWorldSpeech.sayHelloXTimes"> test the wildcard * + + + + best transport from {start} to {end} [at blah] [x test] + + + ({amytime}|{amydatetime}) + + + + + + + + + + + When are you leaving? + {time} + + + + \ No newline at end of file diff --git a/plugin-manager-api/pom.xml b/plugin-manager-api/pom.xml index 28c719e83..499f2cc25 100644 --- a/plugin-manager-api/pom.xml +++ b/plugin-manager-api/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/.settings/org.eclipse.m2e.core.prefs b/plugins/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/alarmclock/pom.xml b/plugins/alarmclock/pom.xml index b66a6645b..2d71050c5 100644 --- a/plugins/alarmclock/pom.xml +++ b/plugins/alarmclock/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepService.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepService.java index a2bd54f34..b32e9f5a0 100644 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepService.java +++ b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepService.java @@ -28,7 +28,6 @@ import java.io.InputStream; import java.util.HashSet; import java.util.Set; -import java.util.function.Consumer; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; @@ -47,8 +46,6 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; -import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SmarthomeFunctionTopics; -import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.Topics; /** * This class controls the alarm sound file, which is used for the alarm clock @@ -91,21 +88,6 @@ private void init() { } catch (IOException | UnsupportedAudioFileException e) { this.logger.error("Cant load alarm sound", e); } - Consumer muteConsumer = message -> { - switch (message) { - case "true": - this.stopBeeping(); - break; - case "false": - // do nothing - break; - default: - this.logger.warn("unkown message {}", message); - break; - } - }; - String topic = Topics.smarthomeAll(SmarthomeFunctionTopics.MUTE); - this.messageHub.subscribe(topic, muteConsumer); } /** @@ -119,17 +101,6 @@ public Set beep(Alarm alarm) { return this.alarmList; } - /** - * @param timer - * timer from the timer class - * @return returns the list of timers - */ - public Set beep(Timer timer) { - this.timerList.add(timer.getId()); - this.update(); - return this.timerList; - } - /** * @param alarm * alarm from the alarm class @@ -141,17 +112,6 @@ public Set stopBeep(Alarm alarm) { return this.alarmList; } - /** - * @param timer - * timer from the timer class - * @return returns the list of timers - */ - public Set stopBeep(Timer timer) { - this.timerList.remove(timer.getId()); - this.update(); - return this.timerList; - } - private void update() { if (!this.alarmList.isEmpty() || !this.timerList.isEmpty()) { @@ -178,7 +138,7 @@ private void startBeeping() { /** * Stops the audio file, when the timer / alarm is deactivated and sets the beeping variable on false */ - private void stopBeeping() { + void stopBeeping() { if (this.beepPlayer != null && this.beepPlayer.isRunning()) { this.beepPlayer.stop(); } diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogic.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogic.java index 74f7d3da6..8e71f627d 100644 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogic.java +++ b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogic.java @@ -26,7 +26,6 @@ import java.time.LocalDateTime; import java.time.ZonedDateTime; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; @@ -35,9 +34,11 @@ import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService; import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.Topics; /** - * This class implements the logic for all the functions that our alarm clock and timer are capable of + * This class implements the logic for all the functions that our alarm clock is capable of * * @author Patrick Singer, Patrick Gebhardt, Florian Bauer */ @@ -50,9 +51,6 @@ public class AlarmClockLogic implements RunnableService { @Reference private AlarmRegistry alarmStorage; - @Reference - private ITimerStorage timerStorage; - @Reference private TaskScheduler taskScheduler; @@ -63,6 +61,9 @@ public class AlarmClockLogic implements RunnableService { private String alarmS = "Alarm "; + @Reference + private MessageHub messageHub; + /** * Creates a Runnable that plays the alarm sound. License: Attribution 3.0 * http://creativecommons.org/licenses/by-sa/3.0/deed.de Recorded by Daniel Simion @@ -78,30 +79,14 @@ private Runnable createAlarmRunnable(int alarmNumber) { Alarm a = getAlarm(alarmNumber); if (a.isActive() && this.environment.getCurrentLocalDateTime().truncatedTo(ChronoUnit.MINUTES) .isEqual(a.getAlarmTime())) { + String topic = Topics.smarthomeAll("alarm"); + this.messageHub.publish(topic, this.alarmS + alarmNumber + "is ringing"); this.alarmbeep.beep(a); } }; } - /** - * Creates a Runnable that plays the alarm sound. License: Attribution 3.0 - * http://creativecommons.org/licenses/by-sa/3.0/deed.de Recorded by Daniel Simion - * - * @param timerNumber - * the number of the timer in storage - * @return runnable - */ - private Runnable createTimerRunnable(int timerNumber) { - return () -> { - if (this.timerStorage.hasTimer(timerNumber) && this.timerStorage.getTimer(timerNumber).isActive()) { - Timer timer = this.timerStorage.getTimer(timerNumber); - this.alarmbeep.beep(timer); - } - }; - - } - /** * Set new alarm and schedule it * @@ -117,7 +102,7 @@ private Runnable createTimerRunnable(int timerNumber) { protected Alarm setAlarm(int tomorrow, int hour, int minute) { if (Alarm.timeValid(hour, minute)) { tomorrowCheck(tomorrow, hour, minute); - int id = searchId(); + int id = searchAlarmId(); Alarm alarm = new Alarm(id, this.alarmTime, true); this.alarmStorage.save(alarm); Runnable alarmRunnable = createAlarmRunnable(id); @@ -131,7 +116,7 @@ protected Alarm setAlarm(int tomorrow, int hour, int minute) { throw new IllegalArgumentException(); } - private int searchId() { + private int searchAlarmId() { int id = 1; List alarmList = this.alarmStorage.getAll(); alarmList.sort((alarm1, alarm2) -> alarm1.getId() - alarm2.getId()); @@ -145,31 +130,6 @@ private int searchId() { return id; } - /** - * Sets new timer and schedules it - * - * @param hours - * number of hours of the timer - * @param minutes - * number of minutes of the timer - * @param seconds - * number of seconds of the timer - * - * @return counter, hours, minutes, seconds - */ - protected Timer setTimer(int hours, int minutes, int seconds) { - - if (Timer.delayValid(hours, minutes, seconds)) { - int counter = this.timerStorage.incrementTimerCounter(); - Timer timer = new Timer(counter, hours, minutes, seconds, true); - this.timerStorage.storeTimer(timer); - Runnable timerRunnable = createTimerRunnable(counter); - this.taskScheduler.schedule(timerRunnable, timer.getTimerDate().toInstant()); - return timer; - } - throw new IllegalArgumentException(); - } - /** * Delete all alarms and reset alarmCounter * @@ -188,28 +148,6 @@ protected String resetAlarms() { return counter + " alarms deleted"; } - /** - * Delete all timers and reset timerCounter - * - * @return counter counts the existing timers - */ - protected String resetTimers() { - int amount = this.timerStorage.getTimerCounter(); - this.timerStorage.putTimerCounter(0); - int counter = 0; - for (int i = 1; i <= amount; i++) { - if (this.timerStorage.hasTimer(i)) { - counter++; - deactivateTimer(i); - this.timerStorage.deleteTimer(i); - } - } - if (counter == 0) { - return "No timers found"; - } - return counter + " timers deleted"; - } - /** * Delete one alarm * @@ -224,22 +162,6 @@ protected String deleteAlarm(int alarmNumber) { return this.alarmS + a.getId() + " deleted"; } - /** - * Deactivates and deletes the timer with the given timer id - * - * @param timerNumber - * timerNumber in the storage - * @return timerNumber - */ - protected String deleteTimer(int timerNumber) { - if (this.timerStorage.hasTimer(timerNumber)) { - deactivateTimer(timerNumber); - this.timerStorage.deleteTimer(timerNumber); - return "Timer " + timerNumber + " deleted"; - } - throw new NoSuchElementException(); - } - /** * Deactivates specific alarm so it will not go off * @@ -259,27 +181,6 @@ protected String deactivateAlarm(int alarmNumber) { return this.alarmS + alarmNumber + " is already inactive"; } - /** - * Deactivated specific timer so it will not go off - * - * @param timerNumber - * number of the timer - * @return timerNumber - */ - protected String deactivateTimer(int timerNumber) { - if (this.timerStorage.hasTimer(timerNumber)) { - Timer timer = this.timerStorage.getTimer(timerNumber); - if (timer.isActive()) { - this.alarmbeep.stopBeep(timer); - timer.setActive(false); - this.timerStorage.storeTimer(timer); - return "Timer " + timerNumber + " deactivated"; - } - return "Timer " + timerNumber + " is already inactive"; - } - throw new NoSuchElementException(); - } - /** * Activates an existing alarm, so it will ring * @@ -315,18 +216,6 @@ protected Alarm getAlarm(int alarmNumber) { throw new NoSuchElementException(); } - /** - * @param timerNumber - * number of the timer in storage - * @return timerNumber - */ - protected Timer getTimer(int timerNumber) { - if (this.timerStorage.hasTimer(timerNumber)) { - return this.timerStorage.getTimer(timerNumber); - } - throw new NoSuchElementException(); - } - /** * Get all alarms * @@ -338,22 +227,6 @@ protected List getAllAlarms() { return alarmList; } - /** - * Get all timers - * - * @return List of all timers - */ - protected List getAllTimers() { - List allTimers = new ArrayList<>(); - int amount = this.timerStorage.getTimerCounter(); - for (int i = 1; i <= amount; i++) { - if (this.timerStorage.hasTimer(i)) { - allTimers.add(this.timerStorage.getTimer(i)); - } - } - return allTimers; - } - /** * Edit a specific Alarm * @@ -404,6 +277,21 @@ private LocalDateTime tomorrowCheck(int tomorrow, int hour, int minute) { return this.alarmTime; } + /** + * Go through all alarms and deactivate old ones + * + * @return alarm stopped + */ + public String stopRinging() { + List allAlarms = getAllAlarms(); + for (Alarm a : allAlarms) { + if (a.isActive() && a.getAlarmTime().isBefore(this.environment.getCurrentLocalDateTime())) { + deactivateAlarm(a.getId()); + } + } + return "Alarm stopped"; + } + /** * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#start() */ diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResource.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResource.java index c7f37e570..93867ad86 100644 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResource.java +++ b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResource.java @@ -24,7 +24,6 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; import java.net.URI; -import java.time.LocalDateTime; import java.util.List; import java.util.NoSuchElementException; @@ -41,6 +40,7 @@ import javax.ws.rs.core.UriInfo; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.utility.rest.Resource; import de.unistuttgart.iaas.amyassist.amy.utility.rest.ResourceEntity; @@ -59,6 +59,9 @@ public class AlarmClockResource implements Resource { @Reference private AlarmClockLogic logic; + + @Reference + private Environment environment; @Context private UriInfo uri; @@ -118,7 +121,7 @@ public Alarm getAlarm(@PathParam("pathid") int alarmnumber) { public Alarm editAlarm(@PathParam("pathid") int alarmNumber, Alarm alarmInc) { Alarm alarm = alarmInc; int day; - if (LocalDateTime.now().getDayOfMonth() == alarm.getAlarmTime().getDayOfMonth()) { + if (this.environment.getCurrentLocalDateTime().getDayOfMonth() == alarm.getAlarmTime().getDayOfMonth()) { day = -1; } else { day = 1; @@ -175,7 +178,7 @@ public Alarm deleteAlarm(@PathParam("pathid") int alarmNumber) { @Produces(MediaType.APPLICATION_JSON) public Alarm newAlarm(Alarm alarm) { int day; - if (LocalDateTime.now().getDayOfMonth() == alarm.getAlarmTime().getDayOfMonth()) { + if (this.environment.getCurrentLocalDateTime().getDayOfMonth() == alarm.getAlarmTime().getDayOfMonth()) { day = -1; } else { day = 1; diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockSpeech.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockSpeech.java index 9d83720c6..04c6d9ccb 100644 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockSpeech.java +++ b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockSpeech.java @@ -23,9 +23,6 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; import java.time.LocalDateTime; import java.util.List; import java.util.Map; @@ -34,6 +31,9 @@ import org.slf4j.Logger; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; /** * Speech class for alarm clock @@ -44,7 +44,7 @@ public class AlarmClockSpeech { @Reference - private AlarmClockLogic logic; + private AlarmClockLogic alarmLogic; @Reference private Logger logger; @@ -58,9 +58,6 @@ public class AlarmClockSpeech { private static final String TIME_MAP_KEY = "time"; private static final String NUMBER_MAP_KEY = "number"; private static final String DAY_MAP_KEY = "day"; - private static final String TYPE_MAP_KEY = "type"; - - private static final String REGEX_ALARM = "(alarm|alarms)"; /** * Creates new alarm @@ -77,7 +74,7 @@ public String setAlarm(Map entities) { if (entities.get(DAY_MAP_KEY) != null && entities.get(DAY_MAP_KEY).getString().equals(TOMORROW)) { tomoro = 1; } - Alarm alarm = this.logic.setAlarm(tomoro, entities.get(TIME_MAP_KEY).getTime().getHour(), + Alarm alarm = this.alarmLogic.setAlarm(tomoro, entities.get(TIME_MAP_KEY).getTime().getHour(), entities.get(TIME_MAP_KEY).getTime().getMinute()); LocalDateTime time = alarm.getAlarmTime(); String day; @@ -93,34 +90,6 @@ public String setAlarm(Map entities) { } } - /** - * Sets a new timer. You can select between hours, minutes and seconds or combinate them - * - * @param entities - * contains the data input - * @return params for the timer - */ - @Intent - public String setTimer(Map entities) { - int hours = 0; - int minutes = 0; - int seconds = 0; - if (entities.get("hour") != null) { - hours = entities.get("hour").getNumber(); - } - if (entities.get("minute") != null) { - minutes = entities.get("minute").getNumber(); - } - if (entities.get("second") != null) { - seconds = entities.get("second").getNumber(); - } - if (hours == 0 && minutes == 0 && seconds == 0) { - return "No value is set"; - } - Timer timer = this.logic.setTimer(hours, minutes, seconds); - return "Timer " + timer.getId() + " set"; - } - /** * Resets all alarms or timers * @@ -130,9 +99,7 @@ public String setTimer(Map entities) { */ @Intent public String resetAlarmClockObjects(Map entities) { - if (entities.get(TYPE_MAP_KEY).getString().matches(REGEX_ALARM)) - return this.logic.resetAlarms(); - return this.logic.resetTimers(); + return this.alarmLogic.resetAlarms(); } /** @@ -145,9 +112,7 @@ public String resetAlarmClockObjects(Map entities) { @Intent public String deleteAlarmClockObject(Map entities) { try { - if (entities.get(TYPE_MAP_KEY).getString().matches(REGEX_ALARM)) - return this.logic.deleteAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); - return this.logic.deleteTimer(entities.get(NUMBER_MAP_KEY).getNumber()); + return this.alarmLogic.deleteAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); } catch (NoSuchElementException e) { this.logException(e); return ELEMENTNOTFOUND; @@ -164,16 +129,25 @@ public String deleteAlarmClockObject(Map entities) { @Intent public String deactivateAlarmClockObject(Map entities) { try { - if (entities.get(TYPE_MAP_KEY).getString().matches(REGEX_ALARM)) - return this.logic.deactivateAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); - this.logic.deleteTimer(entities.get(NUMBER_MAP_KEY).getNumber()); - return "Timer " + entities.get(NUMBER_MAP_KEY).getNumber() + " deactivated"; + return this.alarmLogic.deactivateAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); } catch (NoSuchElementException e) { this.logException(e); return ELEMENTNOTFOUND; } } + /** + * Stops the currently ringing alarm + * + * @param entities + * contains the data input + * @return if successful + */ + @Intent + public String stopRinging(Map entities) { + return this.alarmLogic.stopRinging(); + } + /** * Activates one specific alarm or timer * @@ -184,7 +158,7 @@ public String deactivateAlarmClockObject(Map entities) { @Intent public String activateAlarm(Map entities) { try { - return this.logic.activateAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); + return this.alarmLogic.activateAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); } catch (NoSuchElementException e) { this.logException(e); return ELEMENTNOTFOUND; @@ -201,12 +175,9 @@ public String activateAlarm(Map entities) { @Intent public String getAlarmClockObject(Map entities) { try { - if (entities.get(TYPE_MAP_KEY).getString().matches(REGEX_ALARM)) { - Alarm alarm = this.logic.getAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); - return outputAlarm(alarm); - } - Timer timer = this.logic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber()); - return outputTimer(timer); + Alarm alarm = this.alarmLogic.getAlarm(entities.get(NUMBER_MAP_KEY).getNumber()); + return outputAlarm(alarm); + } catch (NoSuchElementException e) { this.logException(e); return ELEMENTNOTFOUND; @@ -222,26 +193,16 @@ public String getAlarmClockObject(Map entities) { */ @Intent public String getAllAlarmClockObjects(Map entities) { - if (entities.get(TYPE_MAP_KEY).getString().matches(REGEX_ALARM)) { - List alarms = this.logic.getAllAlarms(); - if (alarms.isEmpty()) { - return "No alarms found"; - } - String[] stringAlarms = new String[alarms.size()]; - for (int i = 0; i < alarms.size(); i++) { - stringAlarms[i] = outputAlarm(alarms.get(i)); - } - return String.join("\n", stringAlarms); - } - List timers = this.logic.getAllTimers(); - if (timers.isEmpty()) { - return "No timers found"; + List alarms = this.alarmLogic.getAllAlarms(); + if (alarms.isEmpty()) { + return "No alarms found"; } - String[] stringTimers = new String[timers.size()]; - for (int i = 0; i < timers.size(); i++) { - stringTimers[i] = outputTimer(timers.get(i)); + String[] stringAlarms = new String[alarms.size()]; + for (int i = 0; i < alarms.size(); i++) { + stringAlarms[i] = outputAlarm(alarms.get(i)); } - return String.join("\n", stringTimers); + return String.join("\n", stringAlarms); + } /** @@ -258,7 +219,7 @@ public String editAlarm(Map entities) { day = 1; } try { - return outputAlarm(this.logic.editAlarm(entities.get(NUMBER_MAP_KEY).getNumber(), day, + return outputAlarm(this.alarmLogic.editAlarm(entities.get(NUMBER_MAP_KEY).getNumber(), day, entities.get(TIME_MAP_KEY).getTime().getHour(), entities.get(TIME_MAP_KEY).getTime().getMinute())); } catch (NoSuchElementException e) { this.logException(e); @@ -266,23 +227,6 @@ public String editAlarm(Map entities) { } } - /** - * Gets remaining timer delay - * - * @param entities - * contains the data input - * @return remaining timer delay - */ - @Intent - public String getRemainingTimerDelay(Map entities) { - try { - return outputTimer(this.logic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber())); - } catch (NoSuchElementException e) { - this.logException(e); - return ELEMENTNOTFOUND; - } - } - private void logException(Exception e) { this.logger.error("Exception Thrown!", e); } @@ -303,20 +247,4 @@ private static String outputAlarm(Alarm alarm) { + " but will not ring"; } - private static String outputTimer(Timer timer) { - int[] timeDiff = timer.getRemainingTime(); - int timerNumber = timer.getId(); - if (timeDiff[0] < 0 || timeDiff[1] < 0 || timeDiff[2] < 0) { - return "Timer " + timerNumber + " is ringing right now."; - } - if (timeDiff[0] == 0) { - if (timeDiff[1] == 0) { - return "Timer " + timerNumber + " will ring in " + timeDiff[2] + " seconds"; - } - return "Timer " + timerNumber + " will ring in " + timeDiff[1] + " minutes and " + timeDiff[2] + " seconds"; - } - return "Timer " + timerNumber + " will ring in " + timeDiff[0] + " hours and " + timeDiff[1] + " minutes and " - + timeDiff[2] + " seconds"; - } - } diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmMessageReceiver.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmMessageReceiver.java new file mode 100644 index 000000000..9454e3842 --- /dev/null +++ b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmMessageReceiver.java @@ -0,0 +1,66 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceiver; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.Subscription; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SmarthomeFunctionTopics; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SystemTopics; + +/** + * This class controls the alarm sound file, which is used for the alarm clock + * + * @author Patrick Gebhardt + */ +@MessageReceiver +public class AlarmMessageReceiver { + + @Reference + private AlarmBeepService beepService; + + @Reference + private Logger logger; + + /** + * @param message + * message for the system + */ + @Subscription(SystemTopics.SMARTHOME + "/+/+/" + SmarthomeFunctionTopics.MUTE) + public void beepMessage(String message) { + switch (message) { + case "true": + this.beepService.stopBeeping(); + break; + case "false": + // do nothing + break; + default: + this.logger.warn("unkown message {}", message); + break; + } + } +} diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/ITimerStorage.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/ITimerStorage.java deleted file mode 100644 index d31c52d1b..000000000 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/ITimerStorage.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; - -import java.util.NoSuchElementException; - -/** - * Interface that defines the methods the alarmclock logic needs to store the alarms and timers - * - * @author Patrick Singer, Patrick Gebhardt, Florian Bauer - * - */ -public interface ITimerStorage { - - /** - * Stores a timer in the storage - * - * @param timer - * the timer to be stored - */ - public void storeTimer(Timer timer); - - /** - * Returns the current value of the timer counter in the storage - * - * @return timer counter - */ - public int getTimerCounter(); - - /** - * Sets the timer counter in the storage - * - * @param number - * new number for the timer counter - */ - public void putTimerCounter(int number); - - /** - * Increments and returns the incremented timer counter - * - * @return incremented timer counter - */ - public int incrementTimerCounter(); - - /** - * Checks if storage has a timer with given id - * - * @param id - * timer id - * @return true, if timer with given id exists - */ - public boolean hasTimer(int id); - - /** - * Deletes timer with given id - * - * @param id - * timer id - * @throws NoSuchElementException - * if timer with given id is non existent - */ - public void deleteTimer(int id); - - /** - * Returns the timer with the given id from the storage - * - * @param id - * id of the counter - * @return timer with the given id - * @throws NoSuchElementException - * if timer with given id is non existent - */ - public Timer getTimer(int id); -} diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/Timer.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/Timer.java deleted file mode 100644 index 7cc0069d4..000000000 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/Timer.java +++ /dev/null @@ -1,201 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; - -import java.util.Calendar; - -/** - * Class that defines alarm attributes and behaviour - * - * @author Patrick Singer, Patrick Gebhardt, Florian Bauer - * - */ -public class Timer { - - private int id; - private Calendar timerDate; - private boolean active; - - /** - * Constructor for a timer object - * - * @param id - * id of the timer - * @param hours - * hour delay - * @param minutes - * minute delay - * @param seconds - * second delay - * @param active - * timer active - */ - public Timer(int id, int hours, int minutes, int seconds, boolean active) { - if (id < 1) - throw new IllegalArgumentException(); - this.id = id; - - if (delayValid(hours, minutes, seconds)) { - this.timerDate = Calendar.getInstance(); - this.timerDate.add(Calendar.HOUR, hours); - this.timerDate.add(Calendar.MINUTE, minutes); - this.timerDate.add(Calendar.SECOND, seconds); - } else { - throw new IllegalArgumentException(); - } - - this.active = active; - } - - /** - * Alternative constructor for a timer object - * - * @param id - * id of the timer - * @param timerDate - * date the timer rings - * @param active - * timer active - */ - public Timer(int id, Calendar timerDate, boolean active) { - if (id < 1) - throw new IllegalArgumentException(); - this.id = id; - this.timerDate = timerDate; - this.active = active; - } - - /** - * Returns a string representation of this object - * - * @see java.lang.Object#toString() - * - * @return String representation of this timer object - */ - @Override - public String toString() { - return this.id + ":" + this.timerDate.getTimeInMillis() + ":" + this.active; - } - - /** - * Construct an alarm object from the String that was made by the convertToString method - * - * @param input - * the String made by convertToString method - * @return the corresponding alarm object - */ - public static Timer reconstructObject(String input) { - String[] params = input.split(":"); - if (params.length == 3) { - Calendar timerDate = Calendar.getInstance(); - timerDate.setTimeInMillis(Long.parseLong(params[1])); - return new Timer(Integer.parseInt(params[0]), timerDate, Boolean.parseBoolean(params[2])); - } - throw new IllegalArgumentException(); - } - - /** - * Returns this timers delay until it goes off - * - * @return hourDiff, minuteDiff, secondDiff - */ - public int[] getRemainingTime() { - Calendar current = Calendar.getInstance(); - Calendar future = this.timerDate; - - long diff = future.getTimeInMillis() - current.getTimeInMillis(); - diff /= 1000; - - long hourDiff = diff / 3600; - diff %= 3600; - - long minuteDiff = diff / 60; - diff %= 60; - - long secondDiff = diff; - - return new int[] { (int) hourDiff, (int) minuteDiff, (int) secondDiff }; - } - - /** - * Checks if the given hours, minutes and seconds are valid for a timer - * - * @param hours - * hour delay of timer - * @param minutes - * minute delay of timer - * @param seconds - * second delay of timer - * @return true, if delay is valid, else false - */ - public static boolean delayValid(int hours, int minutes, int seconds) { - return hours >= 0 && minutes >= 0 && seconds >= 0 && hours + minutes + seconds > 0; - } - - /** - * @return id - */ - public int getId() { - return this.id; - } - - /** - * @param id - * timer id - */ - public void setId(int id) { - this.id = id; - } - - /** - * @return timerDate - */ - public Calendar getTimerDate() { - return this.timerDate; - } - - /** - * @param timerDate - * date the timer rings - */ - public void setTimerDate(Calendar timerDate) { - this.timerDate = timerDate; - } - - /** - * @return active - */ - public boolean isActive() { - return this.active; - } - - /** - * @param active - * timer active - */ - public void setActive(boolean active) { - this.active = active; - } - -} diff --git a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerStorage.java b/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerStorage.java deleted file mode 100644 index f7c345466..000000000 --- a/plugins/alarmclock/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerStorage.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; - -import java.util.NoSuchElementException; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; -import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; - -/** - * Implements the IAlarmStorage - * - * @author Patrick Singer, Patrick Gebhardt, Florian Bauer - * - */ -@Service -public class TimerStorage implements ITimerStorage { - - /** - * Core storage object that is instantiated via Dependency Injection - */ - @Reference - IStorage storage; - - private String timerS = "timer"; - - /** - * Storage key for the timer counter value - */ - protected static final String TIMERCOUNTER = "timerCounter"; - - @Override - public void storeTimer(Timer timer) { - this.storage.put(this.timerS + timer.getId(), timer.toString()); - } - - @Override - public int getTimerCounter() { - return Integer.parseInt(this.storage.get(TIMERCOUNTER)); - } - - @Override - public void putTimerCounter(int number) { - this.storage.put(TIMERCOUNTER, number + ""); - } - - @Override - public int incrementTimerCounter() { - int counter = Integer.parseInt(this.storage.get(TIMERCOUNTER)); - counter++; - this.storage.put(TIMERCOUNTER, Integer.toString(counter)); - return counter; - } - - @Override - public boolean hasTimer(int id) { - return this.storage.has(this.timerS + id); - } - - @Override - public void deleteTimer(int id) { - if (this.storage.has(this.timerS + id)) - this.storage.delete(this.timerS + id); - else - throw new NoSuchElementException(); - } - - @Override - public Timer getTimer(int id) { - if (this.storage.has(this.timerS + id)) { - String timerString = this.storage.get(this.timerS + id); - return Timer.reconstructObject(timerString); - } - throw new NoSuchElementException(); - } - - /** - * - * Initialization method for logic class. - */ - @PostConstruct - public void init() { - if (!this.storage.has(TIMERCOUNTER)) - this.storage.put(TIMERCOUNTER, "0"); - } - -} diff --git a/plugins/alarmclock/src/main/resources/AlarmclockSpeech.aim.xml b/plugins/alarmclock/src/main/resources/AlarmclockSpeech.aim.xml index e9d900718..1fd8d6a9f 100644 --- a/plugins/alarmclock/src/main/resources/AlarmclockSpeech.aim.xml +++ b/plugins/alarmclock/src/main/resources/AlarmclockSpeech.aim.xml @@ -2,7 +2,7 @@ - (set|create) (alarm) [(at|for|on) {time}] [{day}] + [set|create] [an|a|new] alarm [(at|for|on) {time}] [{day}] {amytime} @@ -12,60 +12,36 @@ - On what time of day should I set? + What time do you want the alarm to ring? [(at|for|on)] {time} - - (set|create) timer (for|on|at) [{hour} hour] [{minute} minute] [{second}second] - - - - {amyinteger} - - - {amyinteger} - - - {amyinteger} - - - - delete all {type} - - - (alarms|timers) - - + (delete|reset) all alarms - delete {type} {number} + delete alarm {number} - - (alarm|timer) - {amyinteger} - - deactivate {type} {number} + (deactivate|stop) alarm {number} - - (alarm|timer) - {amyinteger} + + stop alarm + @@ -79,27 +55,17 @@ - get {type} {number} + (get|read) alarm [number] {number} - - (alarm|timer) - {amyinteger} - - get all {type} - - - (alarms|timers) - - + get all alarms - edit alarm {number} to {time} [{day}] @@ -116,14 +82,4 @@ - - when (does|is) timer {number} (ringing|ring) - - - {amyinteger} - - - - \ No newline at end of file diff --git a/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services index 155e2c91b..c690768a1 100644 --- a/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services +++ b/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -1,4 +1,3 @@ de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock.AlarmBeepService de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock.AlarmClockLogic -de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock.TimerStorage de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock.AlarmRegistryImpl \ No newline at end of file diff --git a/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers b/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers new file mode 100644 index 000000000..7f5b32472 --- /dev/null +++ b/plugins/alarmclock/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock.AlarmMessageReceiver \ No newline at end of file diff --git a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepServiceTest.java b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepServiceTest.java index f07a034a2..6d4f4fa2a 100644 --- a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepServiceTest.java +++ b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmBeepServiceTest.java @@ -23,10 +23,9 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; import java.time.LocalDateTime; import java.util.HashSet; @@ -53,8 +52,6 @@ public class AlarmBeepServiceTest { private Set alarms = new HashSet<>(); - private Set timers = new HashSet<>(); - /** * Initializes the class variables before each test */ @@ -77,19 +74,6 @@ public void beepAlarmTest() { assertThat(this.abs.beep(alarm).size(), is(1)); } - /** - * Tests beep(Alarm) - */ - @Test - public void beepTimerTest() { - Timer timer = new Timer(1, 1, 1, 1, true); - this.abs.beep(timer); - when(this.abs.beep(timer)).thenReturn(this.timers); - this.timers.add(timer.getId()); - this.abs.beep(timer); - assertThat(this.abs.beep(timer).size(), is(1)); - } - /** * Tests stopBeep(alarm) */ @@ -103,16 +87,4 @@ public void stopBeepAlarmTest() { assertThat(this.abs.stopBeep(alarm).size(), is(0)); } - /** - * Tests stopBeep(timer) - */ - @Test - public void stopBeepTimerTest() { - Timer timer = new Timer(1, 1, 1, 1, true); - when(this.abs.stopBeep(timer)).thenReturn(this.timers); - this.abs.stopBeep(timer); - verify(this.abs).stopBeep(timer); - assertThat(this.abs.stopBeep(timer).size(), is(0)); - } - } diff --git a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogicTest.java b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogicTest.java index 28eb4df99..19f45fc06 100644 --- a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogicTest.java +++ b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockLogicTest.java @@ -23,23 +23,16 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.only; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Calendar; import java.util.List; import java.util.NoSuchElementException; @@ -51,6 +44,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; @@ -73,10 +67,10 @@ public class AlarmClockLogicTest { private AlarmRegistry alarmStorage; - private ITimerStorage timerStorage; - private Environment env; + private MessageHub messageHub; + private List alarms = new ArrayList<>(); private int alarmNumber; @@ -88,9 +82,9 @@ public class AlarmClockLogicTest { public void setup() { this.env = this.framework.mockService(Environment.class); this.scheduler = this.framework.mockService(TaskScheduler.class); - this.timerStorage = this.framework.mockService(ITimerStorage.class); this.setAbs(this.framework.mockService(AlarmBeepService.class)); this.alarmStorage = this.framework.mockService(AlarmRegistry.class); + this.messageHub = this.framework.mockService(MessageHub.class); this.acl = this.framework.setServiceUnderTest(AlarmClockLogic.class); when(this.alarmStorage.getAll()).thenReturn(this.alarms); @@ -115,28 +109,6 @@ public void testSetAlarmInvalid() { assertThrows(IllegalArgumentException.class, () -> this.acl.setAlarm(1, 24, 40)); } - /** - * Tests setTimer with normal arguments - */ - @Test - public void testSetTimer() { - - when(this.timerStorage.incrementTimerCounter()).thenReturn(1); - this.acl.setTimer(12, 35, 40); - verify(this.timerStorage).incrementTimerCounter(); - verify(this.timerStorage).storeTimer(ArgumentMatchers.any(Timer.class)); - verify(this.scheduler).schedule(ArgumentMatchers.any(Runnable.class), ArgumentMatchers.any(Instant.class)); - - } - - /** - * Tests setTimer with invalid arguments - */ - @Test - public void testSetTimerInvalid() { - assertThrows(IllegalArgumentException.class, () -> this.acl.setTimer(0, 0, 0)); - } - /** * Tests resetAlarms */ @@ -156,32 +128,6 @@ public void testResetAlarmsNoAlarms() { assertThat(this.acl.resetAlarms(), is("No alarms found")); } - /** - * Tests resetTimers - */ - @Test - protected void testResetTimers() { - when(this.timerStorage.getTimerCounter()).thenReturn(10); - when(this.timerStorage.hasTimer(2)).thenReturn(true); - when(this.timerStorage.hasTimer(6)).thenReturn(true); - when(this.timerStorage.getTimer(2)).thenReturn(new Timer(2, Calendar.getInstance(), true)); - when(this.timerStorage.getTimer(6)).thenReturn(new Timer(6, Calendar.getInstance(), true)); - - assertThat(this.acl.resetTimers(), is("2 timers deleted")); - verify(this.timerStorage).putTimerCounter(0); - verify(this.timerStorage, times(12)).hasTimer(ArgumentMatchers.anyInt()); - verify(this.timerStorage).deleteTimer(2); - verify(this.timerStorage).deleteTimer(6); - } - - /** - * Tests resetTimers with no timers - */ - @Test - protected void testResetTimersNoTimers() { - assertThat(this.acl.resetTimers(), is("No timers found")); - } - /** * Tests deleteAlarm */ @@ -201,28 +147,6 @@ protected void testDeleteAlarmNotFound() { assertThrows(NoSuchElementException.class, () -> this.acl.deleteAlarm(4)); } - /** - * Tests deleteTimer - */ - @Test - protected void testDeleteTimer() { - when(this.timerStorage.hasTimer(2)).thenReturn(true); - when(this.timerStorage.getTimer(2)).thenReturn(new Timer(2, Calendar.getInstance(), true)); - assertThat(this.acl.deleteTimer(2), is("Timer 2 deleted")); - verify(this.timerStorage, times(2)).hasTimer(2); - verify(this.timerStorage).deleteTimer(2); - } - - /** - * Tests deleteTimer with non existent timer - */ - @Test - protected void testDeleteTimerNotFound() { - assertThrows(NoSuchElementException.class, () -> this.acl.deleteTimer(4)); - verify(this.timerStorage).hasTimer(4); - verify(this.timerStorage, never()).deleteTimer(4); - } - /** * Tests deactivateAlarm with active alarm */ @@ -255,47 +179,6 @@ protected void testDeactivateAlarmNotFound() { assertThrows(NoSuchElementException.class, () -> this.acl.deactivateAlarm(this.alarmNumber)); } - /** - * Tests deactivateTimer with active timer - */ - @Test - protected void testDeactivateTimerActive() { - Timer timer2 = new Timer(2, 12, 0, 0, true); - - when(this.timerStorage.hasTimer(2)).thenReturn(true); - when(this.timerStorage.getTimer(2)).thenReturn(timer2); - - assertThat(this.acl.deactivateTimer(2), is("Timer 2 deactivated")); - verify(this.timerStorage).hasTimer(2); - verify(this.timerStorage).getTimer(2); - verify(this.timerStorage).storeTimer(timer2); - } - - /** - * Tests deactivateTimer with inactive timer - */ - @Test - protected void testDeactivateTimerInactive() { - Timer timer8 = new Timer(8, 22, 57, 44, false); - - when(this.timerStorage.hasTimer(8)).thenReturn(true); - when(this.timerStorage.getTimer(8)).thenReturn(timer8); - - assertThat(this.acl.deactivateTimer(8), is("Timer 8 is already inactive")); - verify(this.timerStorage).hasTimer(8); - verify(this.timerStorage).getTimer(8); - verify(this.timerStorage, never()).storeTimer(ArgumentMatchers.any(Timer.class)); - } - - /** - * Tests deactivateTimer with non existent timer - */ - @Test - protected void testDeactivateTimerNotFound() { - assertThrows(NoSuchElementException.class, () -> this.acl.deactivateTimer(10)); - verify(this.timerStorage, only()).hasTimer(10); - } - /** * Tests activateAlarm with active alarm */ @@ -328,15 +211,6 @@ protected void testActivateAlarmNotFound() { assertThrows(NoSuchElementException.class, () -> this.acl.activateAlarm(this.alarmNumber)); } - /** - * Tests activateTimer with non existent alarm - */ - @Test - protected void testActivateTimerNotFound() { - assertThrows(NoSuchElementException.class, () -> this.acl.deactivateTimer(10)); - verify(this.timerStorage, only()).hasTimer(10); - } - /** * Tests getAlarm with valid argument */ @@ -358,30 +232,6 @@ protected void testGetAlarmNotFound() { assertThrows(NoSuchElementException.class, () -> this.acl.getAlarm(this.alarmNumber)); } - /** - * Tests getTimer with valid argument - */ - @Test - protected void testGetTimer() { - Timer timer1 = new Timer(1, 15, 20, 15, true); - - when(this.timerStorage.hasTimer(1)).thenReturn(true); - when(this.timerStorage.getTimer(1)).thenReturn(timer1); - assertThat(this.acl.getTimer(1), is(timer1)); - verify(this.timerStorage).hasTimer(1); - verify(this.timerStorage).getTimer(1); - reset(this.timerStorage); - } - - /** - * Tests getTimer with non existent timer - */ - @Test - protected void testGetTimerNotFound() { - assertThrows(NoSuchElementException.class, () -> this.acl.getTimer(3)); - verify(this.timerStorage, only()).hasTimer(3); - } - /** * Tests getAllAlarms with some alarms */ @@ -401,39 +251,6 @@ protected void testGetAllAlarmsNoAlarms() { assertThat(returnedAlarms, hasSize(0)); } - /** - * Tests getAllTimers with some timers - */ - @Test - protected void testGetAllTimers() { - Timer timer1 = new Timer(1, 20, 5, 15, true); - Timer timer2 = new Timer(2, 0, 6, 30, false); - Timer timer5 = new Timer(5, 30, 10, 0, true); - List timers = new ArrayList<>(); - timers.add(timer1); - timers.add(timer2); - timers.add(timer5); - when(this.timerStorage.getTimerCounter()).thenReturn(10); - when(this.timerStorage.hasTimer(1)).thenReturn(true); - when(this.timerStorage.getTimer(1)).thenReturn(timer1); - when(this.timerStorage.hasTimer(2)).thenReturn(true); - when(this.timerStorage.getTimer(2)).thenReturn(timer2); - when(this.timerStorage.hasTimer(5)).thenReturn(true); - when(this.timerStorage.getTimer(5)).thenReturn(timer5); - - assertThat(this.acl.getAllTimers(), is(timers)); - verify(this.timerStorage, times(10)).hasTimer(ArgumentMatchers.anyInt()); - } - - /** - * Tests getAllTimers with no timers - */ - @Test - protected void testGetAllTimersNoTimers() { - when(this.timerStorage.getTimerCounter()).thenReturn(10); - assertThat(this.acl.getAllTimers(), is(new ArrayList())); - } - /** * Tests editAlarm with valid arguments */ diff --git a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResourceTest.java b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResourceTest.java index 1a744cccf..3b2d48a01 100644 --- a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResourceTest.java +++ b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockResourceTest.java @@ -45,6 +45,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; @@ -58,6 +59,8 @@ class AlarmClockResourceTest { @Reference private TestFramework framework; + + private Environment environment; private AlarmClockLogic acl; @@ -77,7 +80,8 @@ public void setUp() { this.acl = this.framework.mockService(AlarmClockLogic.class); this.target = this.framework.setRESTResource(AlarmClockResource.class); this.alarmStorage = this.framework.mockService(AlarmRegistry.class); - + this.environment = this.framework.mockService(Environment.class); + when(this.alarmStorage.getAll()).thenReturn(this.alarms); } @@ -139,8 +143,9 @@ void testResetAlarms() { */ @Test void testNewAlarm() { - Alarm newAlarm = new Alarm(1, LocalDateTime.of(2018, 8, 21, 21, 21), true); + Alarm newAlarm = new Alarm(1, LocalDateTime.of(2018, 7, 21, 21, 21), true); when(this.acl.setAlarm(1, 21, 21)).thenReturn(newAlarm); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 10, 4, 19, 19)); Entity entity = Entity.entity(newAlarm, MediaType.APPLICATION_JSON); this.r = this.target.path("alarms/new").request().post(entity); diff --git a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockStorageTest.java b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockStorageTest.java index 23d845a25..0bf5819ac 100644 --- a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockStorageTest.java +++ b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/AlarmClockStorageTest.java @@ -23,21 +23,15 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.only; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; import java.time.LocalDateTime; -import java.util.NoSuchElementException; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentMatchers; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; @@ -58,8 +52,6 @@ public class AlarmClockStorageTest { @Reference private TestFramework framework; - private TimerStorage acs; - private IStorage storage; private static final String TIMERCOUNTER = "timerCounter"; @@ -70,7 +62,6 @@ public class AlarmClockStorageTest { @BeforeEach void setup() { this.storage = this.framework.storage(); - this.acs = this.framework.setServiceUnderTest(TimerStorage.class); this.alarmStorage = this.framework.mockService(AlarmRegistry.class); reset(this.storage); @@ -89,67 +80,6 @@ void testStoreAlarm() { verify(this.alarmStorage).save(alarm); } - /** - * Tests the store timer method - */ - @Test - void testStoreTimer() { - Timer timer = new Timer(3, 1, 0, 1, false); - - this.acs.storeTimer(timer); - verify(this.storage).put("timer3", timer.toString()); - } - - /** - * Tests the getTimerCounter method - */ - @Test - void testGetTimerCounter() { - this.storage.put(TIMERCOUNTER, "20"); - reset(this.storage); - - assertThat(this.acs.getTimerCounter(), is(20)); - verify(this.storage, only()).get(TIMERCOUNTER); - } - - /** - * Tests the putAlarmCounter - */ - @Test - void testPutTimerCounter() { - this.storage.put(TIMERCOUNTER, "10"); - reset(this.storage); - - this.acs.putTimerCounter(20); - verify(this.storage, only()).put(TIMERCOUNTER, "20"); - assertThat(this.storage.get(TIMERCOUNTER), is("20")); - } - - /** - * Tests the incrementTimerCounter method - */ - @Test - void testIncrementTimerCounter() { - this.storage.put(TIMERCOUNTER, "9"); - reset(this.storage); - - assertThat(10, is(this.acs.incrementTimerCounter())); - verify(this.storage).get(TIMERCOUNTER); - verify(this.storage).put(TIMERCOUNTER, "10"); - assertThat(this.storage.get(TIMERCOUNTER), is("10")); - } - - /** - * Tests hasTimer - */ - @Test - public void testHasTimer() { - this.storage.put("timer1", "foo"); - assertThat(true, is(this.acs.hasTimer(1))); - assertThat(false, is(this.acs.hasTimer(2))); - verify(this.storage, times(2)).has(ArgumentMatchers.anyString()); - } - /** * Tests deleteAlarm with normal case */ @@ -176,27 +106,6 @@ public void testDeleteAlarmNotFound() { assertThat(true, is(this.alarmStorage.getAll().isEmpty())); } - /** - * Tests deleteTimer with normal case - */ - @Test - public void testDeleteTimer() { - this.storage.put("timer1", "foo"); - this.acs.deleteTimer(1); - verify(this.storage).has("timer1"); - verify(this.storage).delete("timer1"); - assertThat(false, is(this.storage.has("timer1"))); - } - - /** - * Tests deleteTimer with non existent timer - */ - @Test - public void testDeleteTimerNotFound() { - assertThrows(NoSuchElementException.class, () -> this.acs.deleteTimer(1)); - verify(this.storage, only()).has("timer1"); - } - /** * Tests the getAlarm method */ @@ -208,16 +117,4 @@ void testGetAlarm() { assertThat(alarm.toString(), is("1:2018-08-15T11:11:true")); } - - /** - * Tests the getTimer - */ - @Test - void testGetTimer() { - // wrong id - assertThrows(NoSuchElementException.class, () -> this.acs.getTimer(1)); - - // usual case - this.storage.put("timer1", "1:20:20:20:true"); - } } diff --git a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerTest.java b/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerTest.java deleted file mode 100644 index 5e4091c1c..000000000 --- a/plugins/alarmclock/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/alarmclock/TimerTest.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.alarmclock; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.util.Calendar; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; - -import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; - -/** - * Class to test the Timer class - * - * @author Patrick Singer - */ -@ExtendWith(FrameworkExtension.class) -public class TimerTest { - - /** - * Tests toString - */ - @Test - public void toStringTest() { - Calendar c = Calendar.getInstance(); - assertEquals("1:" + c.getTimeInMillis() + ":true", new Timer(1, c, true).toString()); - } - - /** - * Tests reconstructObject - */ - @Test - public void reconstructObjectTest() { - assertThrows(IllegalArgumentException.class, () -> Timer.reconstructObject("foo")); - - Timer t1 = Timer.reconstructObject("1:1000000:false"); - assertEquals(1, t1.getId()); - assertEquals(1000000, t1.getTimerDate().getTimeInMillis()); - assertEquals(false, t1.isActive()); - } - - /** - * Tests delayValid - */ - @Test - public void delayValidTest() { - assertFalse(Timer.delayValid(0, 0, 0)); - assertFalse(Timer.delayValid(-1, 0, 0)); - assertFalse(Timer.delayValid(0, -1, 0)); - assertFalse(Timer.delayValid(0, 0, -1)); - assertFalse(Timer.delayValid(-1, 5, 5)); - } - -} diff --git a/plugins/calendar/pom.xml b/plugins/calendar/pom.xml index f60731aed..5e47f6b33 100644 --- a/plugins/calendar/pom.xml +++ b/plugins/calendar/pom.xml @@ -8,10 +8,15 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml + + de.unistuttgart.iaas.amyassist + amy-http-server-api + provided + com.google.apis @@ -32,7 +37,6 @@ org.junit.jupiter junit-jupiter-params - diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogic.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogic.java index d592e29a0..ff1fe248d 100644 --- a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogic.java +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogic.java @@ -23,17 +23,10 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.calendar; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.time.chrono.ChronoLocalDate; +import java.time.*; import java.time.format.DateTimeFormatter; -import java.time.format.TextStyle; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import org.slf4j.Logger; @@ -62,18 +55,9 @@ public class CalendarLogic { @Reference private Environment environment; - private enum OutputCase { - STARTINPAST, STARTINFUTURE, ALLDAYLONG, SINGLEDAY - } - private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSS"); private LocalTime zero = LocalTime.of(0, 0, 0, 0); - /** - * Natural language response of Amy when the event list is empty. - */ - String noEventsFound = "No upcoming events found."; - /** * This method creates an event for the connected google calendar, modified version from * https://developers.google.com/calendar/create-events @@ -115,96 +99,19 @@ public void setEvent(CalendarEvent calendarEvent) { * number of events the user wants to get * @return event summary */ - public String getEvents(int number) { - List eventList = new ArrayList<>(); - DateTime current = new DateTime(System.currentTimeMillis()); + public List getEvents(int number) { + List eventList = new ArrayList<>(); + LocalDateTime now = this.environment.getCurrentLocalDateTime(); + DateTime current = getDateTime(now, 0); Events events = this.calendarService.getEvents(current, number); List items = events.getItems(); - if (items.isEmpty()) { - return this.noEventsFound; - } - LocalDateTime now = LocalDateTime.now(); for (Event event : items) { - eventList.add(checkDay(now, event, true)); - } - if (number == 1) { - return "You have following upcoming event:\n" + String.join("\n", eventList); - } - return "You have following upcoming " + number + " events:\n" + String.join("\n", eventList); - - } - - /** - * This method contains the logic to show the calendar events today - * - * @return the events of today - */ - public String getEventsToday() { - List eventList = new ArrayList<>(); - LocalDateTime now = this.environment.getCurrentLocalDateTime(); - DateTime min = new DateTime(System.currentTimeMillis()); - DateTime max = getDateTime(now, 1); - Events events = this.calendarService.getEvents(min, max); - List items = events.getItems(); - if (items.isEmpty()) { - return this.noEventsFound; - } - for (Event event : items) { - eventList.add(this.checkDay(now, event, false)); - } - if (eventList.isEmpty()) { - return "There are no events today."; - } - return "You have following events today:\n" + String.join("\n", eventList); - } - - /** - * This method contains the logic to show the calendar events tomorrow - * - * @return the events of tomorrow - */ - public String getEventsTomorrow() { - List eventList = new ArrayList<>(); - LocalDateTime now = this.environment.getCurrentLocalDateTime(); - DateTime min = getDateTime(now, 1); - DateTime max = getDateTime(now, 2); - Events events = this.calendarService.getEvents(min, max); - List items = events.getItems(); - if (items.isEmpty()) { - return this.noEventsFound; - } - for (Event event : items) { - eventList.add(this.checkDay(now, event, false)); - } - if (eventList.isEmpty()) { - return "There are no events tomorrow."; - } - return "You have following events tomorrow:\n" + String.join("\n", eventList); - } - - /** - * This method contains the logic to show the calendar events on a specific date as natural language output - * - * @param chosenDay - * LocalDateTime variable - * @return the events of the chosen day - */ - public String getEventsAtAsString(LocalDateTime chosenDay) { - List eventList = new ArrayList<>(); - DateTime min = getDateTime(chosenDay, 0); - DateTime max = getDateTime(chosenDay, 1); - Events events = this.calendarService.getEvents(min, max); - List items = events.getItems(); - if (items.isEmpty()) { - return this.noEventsFound; - } - for (Event event : items) { - eventList.add(this.checkDay(chosenDay, event, false)); - } - if (eventList.isEmpty()) { - return "There are no events on the " + getDate(chosenDay) + "."; + CalendarEvent calendarEvent = new CalendarEvent(event.getId(), getLocalDateTimeStart(event), + getLocalDateTimeEnd(event), event.getSummary(), event.getLocation(), event.getDescription(), + isAllDay(event)); + eventList.add(calendarEvent); } - return "You have following events on the " + getDate(chosenDay) + ":\n" + String.join("\n", eventList); + return eventList; } /** @@ -238,184 +145,13 @@ public List getEventsAt(LocalDateTime chosenDay) { * the number of days you want to add * @return DateTime of input */ - private DateTime getDateTime(LocalDateTime ldt, int plusDays) { + public DateTime getDateTime(LocalDateTime ldt, int plusDays) { LocalDate nextDay = ldt.plusDays(plusDays).toLocalDate(); LocalDateTime endOfDay = LocalDateTime.of(nextDay, this.zero); ZonedDateTime zdt = endOfDay.atZone(ZoneId.systemDefault()); return new DateTime(zdt.toInstant().toEpochMilli()); } - /** - * This method checks if and how an event is to be displayed in the output. - * - * @param dayToCheck - * the day from which we want to know how the current event belongs to it - * @param event - * the current chosen event - * @param withDate - * if the date should be displayed (or only the time) - * @return the event as natural language text - */ - public String checkDay(LocalDateTime dayToCheck, Event event, boolean withDate) { - LocalDateTime startDateTime = getLocalDateTimeStart(event); - LocalDateTime endDateTime = getLocalDateTimeEnd(event); - LocalDate startDate = getLocalDateStart(event); - LocalDate checkDate = dayToCheck.toLocalDate(); - LocalDate endDate = getLocalDateEnd(event); - OutputCase outputCase; - boolean withTime = !isAllDay(event); - boolean withStartDate = withDate; - boolean withEndDate = withDate; - // check if the beginning and the end of the event is on another day as the current day - if (dayToCheck.isAfter(startDateTime)) { - // event already started - outputCase = OutputCase.STARTINPAST; - if (checkDate.isAfter(startDate) && checkDate.isBefore(endDate)) { - // event didn't start at dayToCheck and won't finish at dayToCheck - withStartDate = true; - withEndDate = true; - } else if (checkDate.isEqual(startDate)) { - // event started at dayToCheck - outputCase = eventType(checkDate, endDate, event, OutputCase.STARTINPAST); - // event will finish on a different date - withEndDate = !checkDate.isEqual(endDate); - } else { - // event ends on dayToCheck - withStartDate = true; - } - } else { - // event will start in future - outputCase = eventType(startDate, endDate, event, OutputCase.STARTINFUTURE); - // event will finish on an other day in the future - withEndDate = !startDate.isEqual(endDate); - } - - return eventToString(startDateTime, endDateTime, event, withStartDate, withEndDate, withTime, outputCase); - } - - /** - * This method formats the date and time of the events and adds them into the list - * - * @param startDate - * start time of the event - * @param endDate - * end time of the event - * @param event - * data of the event - * @param withStartDate - * determines if event output is with or without the start date - * @param withEndDate - * determines if event output is with or without the end date - * @param withTime - * distinguishes if it is an event with or without a time stamp - * @param outputCase - * distinguishes between different output cases - * @return the string generated for the event - */ - public String eventToString(LocalDateTime startDate, LocalDateTime endDate, Event event, boolean withStartDate, - boolean withEndDate, boolean withTime, OutputCase outputCase) { - String eventData = convertEventTitle(event.getSummary()); - String eventStartDate = dateOutput(startDate, withStartDate, withTime); - String eventEndDate = dateOutput(endDate, withEndDate, withTime); - String eventStartTime = ""; - String eventEndTime = ""; - DateTimeFormatter time = DateTimeFormatter.ofPattern("HH:mm"); - if (withTime) { - eventStartTime = " " + time.format(startDate); - eventEndTime = " " + time.format(endDate); - } - switch (outputCase) { - case STARTINPAST: - if (withStartDate || withTime) { - eventData += " since" + eventStartDate + eventStartTime; - } - if (withEndDate || withTime) { - eventData += " until" + eventEndDate + eventEndTime; - } - eventData += ". \n"; - break; - case STARTINFUTURE: - eventData += " from" + eventStartDate + eventStartTime; - eventData += " until" + eventEndDate + eventEndTime + ". \n"; - break; - case ALLDAYLONG: - if (withStartDate) { - eventStartDate = " on the " + getDate(startDate); - } - eventData += eventStartDate + " all day long. \n"; - break; - case SINGLEDAY: - if (withStartDate) { - eventData += " on the " + getDate(startDate) + " at " + time.format(startDate) + " until " - + time.format(endDate) + ". \n"; - } else { - eventData += " from " + time.format(startDate) + " until " + time.format(endDate) + ". \n"; - } - break; - - default: - this.logger.error("Missing or wrong output case."); - break; - } - - return eventData; - } - - /** - * Checks if given event summary is empty/ null and returns an alternative String or the title - * - * @param title the title String of the event - * @return the title part of the event for Amy output - */ - private String convertEventTitle(String title) { - if(title == null || title.equals("")) { - return "An event without a title"; - } - return title; - } - - /** - * @param date - * the date as LocalDateTime - * @param withDate - * if the date should be displayed - * @param withTime - * if there is a time following the date - * @return the date String part of Amys natural language output - */ - public String dateOutput(LocalDateTime date, boolean withDate, boolean withTime) { - String output = ""; - if (withDate) { - output = " the " + getDate(date); - if (withTime) { - output += " at"; - } - } - return output; - } - - /** - * @param date1 - * the first date - * @param date2 - * the second date - * @param event - * the current event - * @param defaultCase - * the default OutputCase - * @return which OutputCase is needed - */ - public static OutputCase eventType(ChronoLocalDate date1, ChronoLocalDate date2, Event event, - OutputCase defaultCase) { - if (date1.isEqual(date2)) { - if (isAllDay(event)) { - return OutputCase.ALLDAYLONG; - } - return OutputCase.SINGLEDAY; - } - return defaultCase; - } - /** * @param event * google calendar event @@ -424,31 +160,7 @@ public static OutputCase eventType(ChronoLocalDate date1, ChronoLocalDate date2, public static boolean isAllDay(Event event) { return event.getStart().getDate() != null; } - - /** - * @param event - * google calendar event - * @return the start of the event as LocalDate - */ - public static LocalDate getLocalDateStart(Event event) { - if (isAllDay(event)) { - return LocalDate.parse(event.getStart().getDate().toString()); - } - return LocalDate.parse(event.getStart().getDateTime().toString().substring(0, 10)); - } - - /** - * @param event - * google calendar event - * @return the end of the event as LocalDate - */ - public static LocalDate getLocalDateEnd(Event event) { - if (isAllDay(event)) { - return LocalDate.parse(event.getEnd().getDate().toString()).minusDays(1); - } - return LocalDate.parse(event.getEnd().getDateTime().toString().substring(0, 10)); - } - + /** * @param event * google calendar event @@ -474,28 +186,4 @@ public LocalDateTime getLocalDateTimeEnd(Event event) { return ZonedDateTime.parse(event.getEnd().getDateTime().toString()).withZoneSameInstant(ZoneId.systemDefault()) .toLocalDateTime(); } - - /** - * @param date - * a date as LocalDate - * @return String in Natural Language of the date, e.g. "12th of May" - */ - public String getDate(LocalDateTime date) { - return ordinal(date.getDayOfMonth()) + " of " + date.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); - } - - /** - * A method to convert the integer day to an ordinal (from 1 to 31) - * - * @param i - * the day as integer - * @return the day as ordinal, e.g. 1st - */ - public static String ordinal(int i) { - String[] ordinals = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; - if (10 < i && i < 14) { - return i + "th"; - } - return i + ordinals[i % 10]; - } } diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarResource.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarResource.java index fd5bf6ec6..0b8a2dd7b 100644 --- a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarResource.java +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarResource.java @@ -86,9 +86,8 @@ public List getEventsAt(@PathParam("ldt") LocalDateTime ldt) { List events = this.logic.getEventsAt(ldt); if(events != null) { return events; - } else { - throw new WebApplicationException("Couldn't get events", Status.NOT_FOUND); } + throw new WebApplicationException("Couldn't get events", Status.NOT_FOUND); } } diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarService.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarService.java index 4cc8e6a28..ec714cd16 100644 --- a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarService.java +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarService.java @@ -32,7 +32,6 @@ import com.google.api.client.auth.oauth2.Credential; import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; -import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; @@ -51,6 +50,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.plugin.calendar.google.VerificationCodeReceiverService; /** * This class authorizes Amy to connect to the google calendar, this is a modified version from @@ -70,6 +70,9 @@ public class CalendarService { @Reference private Environment environment; + @Reference + private VerificationCodeReceiverService codeReceiverService; + private Calendar service; private String primary = "primary"; @@ -86,7 +89,8 @@ public class CalendarService { */ private Credential getCredentials(final NetHttpTransport xHTTPTRANSPORT) throws IOException { // Load client secrets - String clientSecretIn = this.configuration.getProperty("JSON"); + String clientSecretIn = this.configuration.getProperty("CREDENTIALS_JSON"); + String storedCredentialsPath = this.configuration.getProperty("STORED_CREDENTIALS_PATH"); if (clientSecretIn.isEmpty()) { throw new IllegalStateException("Missing client secrets"); } @@ -95,9 +99,10 @@ private Credential getCredentials(final NetHttpTransport xHTTPTRANSPORT) throws GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(xHTTPTRANSPORT, JSON_FACTORY, clientSecrets, SCOPES) .setDataStoreFactory(new FileDataStoreFactory(this.environment.getWorkingDirectory() - .resolve("temp/calendarauth").toAbsolutePath().toFile())) + .resolve(storedCredentialsPath).toAbsolutePath().toFile())) .setAccessType("offline").build(); - return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user"); + return new AuthorizationCodeInstalledApp(flow, this.codeReceiverService.newVerificationCodeReceiver()) + .authorize("user"); } /** diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeech.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeech.java index 9e63e5f69..36ee9092b 100644 --- a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeech.java +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeech.java @@ -23,10 +23,19 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.calendar; +import java.time.*; +import java.time.chrono.ChronoLocalDate; +import java.time.format.TextStyle; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; import java.util.Map; +import org.slf4j.Logger; + import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; @@ -34,61 +43,366 @@ /** * This is the speech class, which contains the commands for the calendar * - * @author Patrick Gebhardt, Florian Bauer + * @author Florian Bauer, Patrick Gebhardt */ @Service @SpeechCommand public class CalendarSpeech { @Reference - private CalendarLogic calendar; + private CalendarLogic logic; + @Reference + private Logger logger; + @Reference + private Environment environment; + + private enum OutputCase { + STARTINPAST, STARTINFUTURE, ALLDAYLONG, SINGLEDAY + } + + private static final LocalTime ZERO = LocalTime.of(0, 0, 0, 0); /** * - * @param entites + * @param entities * from the speech * * @return the upcoming X events from the calendar */ @Intent - public String getEvents(Map entites) { - return this.calendar.getEvents(entites.get("number").getNumber()); + public String getEvents(Map entities) { + List events; + List eventList = new ArrayList<>(); + int number = entities.get("number").getNumber(); + LocalDateTime now = this.environment.getCurrentLocalDateTime(); + events = this.logic.getEvents(number); + if (events.isEmpty()) { + return "No events found."; + } + for (CalendarEvent event : events) { + eventList.add(this.checkDate(now, event, false)); + } + if (number == 1) { + return "You have following upcoming event:\n" + String.join("\n", eventList); + } + return "You have following upcoming " + number + " events:\n" + String.join("\n", eventList); } /** - * @param entites + * @param entities * from the speech - * @return upcoming events today or tomorrow depending on input + * @return events on the chosen date */ @Intent - public String getEventsToday(Map entites) { - if (entites.get("day").getString().equalsIgnoreCase("today")) { - return this.calendar.getEventsToday(); + public String getEventsAt(Map entities) { + List events; + List eventList = new ArrayList<>(); + String dateEntity = entities.get("date").getString(); + LocalDateTime date = LocalDateTime.of(entities.get("date").getDate(), ZERO); + if (entities.get("eventyear") == null && date.toLocalDate().isBefore(this.environment. + getCurrentLocalDateTime().toLocalDate())) { + date = date.withYear(this.environment.getCurrentLocalDateTime().getYear() + 1); } - return this.calendar.getEventsTomorrow(); - + events = this.logic.getEventsAt(date); + for (CalendarEvent event : events) { + eventList.add(this.checkDate(date, event, false)); + } + if (dateEntity.equals("today") || dateEntity.equals("tomorrow")) { + if (events.isEmpty()) { + return "You have no events " + dateEntity + "."; + } + return "You have following events " + dateEntity +":\n" + String.join("\n", eventList); + } + if (events.isEmpty()) { + return "No events found for the " + getDate(date) + " " + date.getYear() + "."; + } + return "You have following events on the " + getDate(date) + " " + date.getYear() + ":\n" + + String.join("\n", eventList); } /** - * example method - * + * This method handles Amy response for creating a new event + * * @param entities - * @return + * from the speech + * @return if the event was successfully created or if not why */ @Intent - public String getADate(Map entities) { - return entities.get("date").getDate().toString(); + public String setEvent(Map entities) { + boolean allDay = isAllDay(entities.get("allday").getString()); + LocalDateTime start; + LocalDateTime end; + + if (allDay) { + start = LocalDateTime.of(entities.get("startdate").getDate().plusDays(1), ZERO); + end = LocalDateTime.of(entities.get("enddate").getDate().plusDays(2), ZERO); + } else if(entities.get("starttime") == null || entities.get("endtime") == null) { + return "You have to restart the creation of a new event and please make sure that you add a time to the " + + "start and to the end if you choose an non all day event."; + } else { + start = LocalDateTime.of(entities.get("startdate").getDate(), entities.get("starttime").getTime()); + end = LocalDateTime.of(entities.get("enddate").getDate(), entities.get("endtime").getTime()); + } + LocalDateTime now = this.environment.getCurrentLocalDateTime(); + if (entities.get("startyear") == null && entities.get("endyear") == null && start.isBefore(now)) { + start = start.withYear(now.getYear() + 1); + end = end.withYear(now.getYear() + 1); + } + + if (end.isBefore(start)) { + return "You have to restart the creation of a new event and please make sure that the start of the event " + + "is before the end."; + } + + String location = entities.get("location").getString(); + if (location.equals("no")) { location = ""; } + String description = entities.get("description").getString(); + if (description.equals("no")) { description = ""; } + + int reminderTime = entities.get("remindertimevalue").getNumber(); + String reminderTimeUnit = entities.get("remindertimeunit").getString(); + if (reminderTimeUnit.equals("hours")) { + reminderTime *= 60; + } + if (reminderTimeUnit.equals("days")) { + reminderTime *= 60 * 24; + } + + String title = entities.get("title").getString(); + this.logic.setEvent(new CalendarEvent(start, end, title, location, description, + entities.get("remindertype").getString(), reminderTime, "", allDay)); + return "I set up the event " + title + " for you."; + } + + /** + * @param date + * a date as LocalDate + * @return String in Natural Language of the date, e.g. "12th of May" + */ + public String getDate(LocalDateTime date) { + return ordinal(date.getDayOfMonth()) + " of " + date.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); } /** - * example method - * - * @param entities - * @return + * A method to convert the integer day to an ordinal (from 1 to 31) + * + * @param i + * the day as integer + * @return the day as ordinal, e.g. 1st */ - @Intent - public String getADateTime(Map entities) { - return entities.get("datetime").getDateTime().toString(); + public static String ordinal(int i) { + String[] ordinals = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; + if (10 < i && i < 14) { + return i + "th"; + } + return i + ordinals[i % 10]; + } + + /** + * This method checks if and how an event is to be displayed in the output. + * + * @param dayToCheck + * the day from which we want to know how the current event belongs to it + * @param event + * the current chosen event + * @param withDate + * if the date should be displayed (or only the time) + * @return the event as natural language text + */ + public String checkDate(LocalDateTime dayToCheck, CalendarEvent event, boolean withDate) { + LocalDateTime startDateTime = event.getStart(); + LocalDateTime endDateTime = event.getEnd(); + LocalDate startDate = event.getStart().toLocalDate(); + LocalDate checkDate = dayToCheck.toLocalDate(); + LocalDate endDate = event.getEnd().toLocalDate(); + OutputCase outputCase; + boolean withTime = !event.isAllDay(); + boolean withStartDate = withDate; + boolean withEndDate = withDate; + // check if the beginning and the end of the event is on another day as the current day + if (dayToCheck.isAfter(startDateTime)) { + // event already started + outputCase = OutputCase.STARTINPAST; + if (checkDate.isAfter(startDate) && checkDate.isBefore(endDate)) { + // event didn't start at dayToCheck and won't finish at dayToCheck + withStartDate = true; + withEndDate = true; + } else if (checkDate.isEqual(startDate)) { + // event started at dayToCheck + outputCase = eventType(checkDate, endDate, event, OutputCase.STARTINPAST); + // event will finish on a different date + withEndDate = !checkDate.isEqual(endDate); + } else { + // event ends on dayToCheck + withStartDate = true; + } + } else { + // event will start in future + outputCase = eventType(startDate, endDate, event, OutputCase.STARTINFUTURE); + // event will finish on an other day in the future + withEndDate = !startDate.isEqual(endDate); + } + + return eventToString(startDateTime, endDateTime, event, withStartDate, withEndDate, withTime, outputCase); + } + + /** + * This method formats the date and time of the events and adds them into the list + * + * @param startDate + * start time of the event + * @param endDate + * end time of the event + * @param event + * data of the event + * @param withStartDate + * determines if event output is with or without the start date + * @param withEndDate + * determines if event output is with or without the end date + * @param withTime + * distinguishes if it is an event with or without a time stamp + * @param outputCase + * distinguishes between different output cases + * @return the string generated for the event + */ + public String eventToString(LocalDateTime startDate, LocalDateTime endDate, CalendarEvent event, boolean withStartDate, + boolean withEndDate, boolean withTime, OutputCase outputCase) { + String eventData = convertEventTitle(event.getSummary()); + String eventStartDate = dateOutput(startDate, withStartDate, withTime); + String eventEndDate = dateOutput(endDate, withEndDate, withTime); + String eventStartTime = ""; + String eventEndTime = ""; + if (withTime) { + eventStartTime = " " + getCorrectTime(startDate); + eventEndTime = " " + getCorrectTime(endDate); + } + switch (outputCase) { + case STARTINPAST: + if (withStartDate || withTime) { + eventData += " since" + eventStartDate + eventStartTime; + } + if (withEndDate || withTime) { + eventData += " until" + eventEndDate + eventEndTime; + } + eventData += ". \n"; + break; + case STARTINFUTURE: + eventData += " from" + eventStartDate + eventStartTime; + eventData += " until" + eventEndDate + eventEndTime + ". \n"; + break; + case ALLDAYLONG: + if (withStartDate) { + eventStartDate = " on the " + getDate(startDate); + } + eventData += eventStartDate + " all day long. \n"; + break; + case SINGLEDAY: + if (withStartDate) { + eventData += " on the " + getDate(startDate) + " at " + getCorrectTime(startDate) + " until " + + getCorrectTime(endDate) + ". \n"; + } else { + eventData += " from " + getCorrectTime(startDate) + " until " + getCorrectTime(endDate) + ". \n"; + } + break; + + default: + this.logger.error("Missing or wrong output case."); + break; + } + + return eventData; + } + + /** + * @param localDateTime + * input time as LocalDateTime + * @return correct time in 12 hour format with am/pm + */ + public String getCorrectTime(LocalDateTime localDateTime) { + if (localDateTime.getHour() > 11) { + return parseTo12Hour(localDateTime) + " pm"; + } + return parseTo12Hour(localDateTime) + " am"; + + } + + /** + * @param localDateTime + * LocalDateTime from where the time is created + * @return LocalTime equivalent in 12 hour format, without am or pm + */ + public LocalTime parseTo12Hour(LocalDateTime localDateTime) { + int hour = localDateTime.getHour(); + int minute = localDateTime.getMinute(); + if (hour > 12) { + hour = hour % 12; + } + return LocalTime.of(hour, minute); + } + /** + * Checks if given event summary is empty/ null and returns an alternative String or the title + * + * @param title + * the title String of the event + * @return the title part of the event for Amy output + */ + private String convertEventTitle(String title) { + if (title == null || title.equals("")) { + return "An event without a title"; + } + return title; + } + + /** + * @param date + * the date as LocalDateTime + * @param withDate + * if the date should be displayed + * @param withTime + * if there is a time following the date + * @return the date String part of Amys natural language output + */ + public String dateOutput(LocalDateTime date, boolean withDate, boolean withTime) { + String output = ""; + if (withDate) { + output = " the " + getDate(date); + if (withTime) { + output += " at"; + } + } + return output; + } + + /** + * @param date1 + * the first date + * @param date2 + * the second date + * @param event + * the current event + * @param defaultCase + * the default OutputCase + * @return which OutputCase is needed + */ + public static OutputCase eventType(ChronoLocalDate date1, ChronoLocalDate date2, CalendarEvent event, + OutputCase defaultCase) { + if (date1.isEqual(date2)) { + if (event.isAllDay()) { + return OutputCase.ALLDAYLONG; + } + return OutputCase.SINGLEDAY; + } + return defaultCase; + } + + /** + * @param allDayEntity + * the String from which the boolean is parsed. + * @return if the event is all day + */ + public static boolean isAllDay(String allDayEntity) { + return allDayEntity.equals("yes") || allDayEntity.equals("true") || allDayEntity.equals("all day"); + } } diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverResource.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverResource.java new file mode 100644 index 000000000..dcb95348c --- /dev/null +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverResource.java @@ -0,0 +1,67 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.calendar.google; + +import java.util.UUID; + +import javax.ws.rs.*; +import javax.ws.rs.core.Response.Status; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; + +/** + * REST Resource for calendar verification code from google + * + * @author Leon Kiefer + */ +@Path("calendar/verification") +public class VerificationCodeReceiverResource { + + @Reference + private VerificationCodeReceiverService service; + + /** + * Get the verificationCode after google oauth. + * + * @param id + * the unique id of the verification code request + * @param code + * the verification code + * @param error + * error or null + * @return landing page + */ + @GET + @Path("code/{id}") + public String verificationCode(@PathParam("id") UUID id, @QueryParam("code") String code, + @QueryParam("error") String error) { + if (error != null) { + this.service.setVerificationCode(id, code); + throw new WebApplicationException("Request contains error: " + error, Status.BAD_REQUEST); + } + this.service.setVerificationCode(id, code); + return "OK"; + } + +} diff --git a/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverService.java b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverService.java new file mode 100644 index 000000000..53dca0e14 --- /dev/null +++ b/plugins/calendar/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/google/VerificationCodeReceiverService.java @@ -0,0 +1,124 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.calendar.google; + +import java.io.IOException; +import java.net.URI; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; + +import javax.ws.rs.core.UriBuilder; + +import org.slf4j.Logger; + +import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService; +import de.unistuttgart.iaas.amyassist.amy.httpserver.Server; + +/** + * Adapter for javax.ws.rs and VerificationCodeReceiver. + * + * @author Leon Kiefer + */ +@Service(VerificationCodeReceiverService.class) +public class VerificationCodeReceiverService implements RunnableService { + + private final Map> openRequests = new ConcurrentHashMap<>(); + + @Reference + private Logger logger; + + @Reference + private Server server; + + public VerificationCodeReceiver newVerificationCodeReceiver() { + return new VerificationCodeReceiver() { + + private final CompletableFuture completableFuture = new CompletableFuture<>(); + private final UUID randomUUID = UUID.randomUUID(); + + @Override + public String waitForCode() throws IOException { + try { + return this.completableFuture.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new IOException("Got interruped while waiting for response.", e); + } catch (ExecutionException e) { + throw new IOException(e); + } + } + + @Override + public void stop() throws IOException { + VerificationCodeReceiverService.this.openRequests.remove(this.randomUUID); + } + + @Override + public String getRedirectUri() throws IOException { + + VerificationCodeReceiverService.this.openRequests.put(this.randomUUID, this.completableFuture); + + URI uri = UriBuilder.fromPath(server.getBaseUrl()).path(VerificationCodeReceiverResource.class) + .path(VerificationCodeReceiverResource.class, "verificationCode").build(this.randomUUID); + + return uri.toString(); + } + }; + } + + /** + * Set the received code. + * + * @param id + * @param code + */ + void setVerificationCode(UUID id, String code) { + synchronized (this.openRequests) { + if (!this.openRequests.containsKey(id)) { + throw new IllegalArgumentException("No verification request found for this id."); + } + this.openRequests.remove(id).complete(code); + } + } + + @Override + public void start() { + // empty + } + + @Override + public void stop() { + if (!this.openRequests.isEmpty()) { + this.logger.warn("There are open Verification code requests!"); + this.openRequests.clear(); + } + } +} diff --git a/plugins/calendar/src/main/resources/CalendarSpeech.aim.xml b/plugins/calendar/src/main/resources/CalendarSpeech.aim.xml index 581518e58..a9abc18a8 100644 --- a/plugins/calendar/src/main/resources/CalendarSpeech.aim.xml +++ b/plugins/calendar/src/main/resources/CalendarSpeech.aim.xml @@ -2,7 +2,7 @@ - what (is|are) my next [{number}] event + [what (is|are)] my next [{number}] (events|appointments) {amyinteger} @@ -10,42 +10,109 @@ How many events should i show? - {number} [event] + {number} [events|appointments] - [which] events [do i have] [{day}] + ref="de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarSpeech.getEventsAt"> + [which|what|do i have] [any] [is in my] (calendar|events|appointments) [do i have] [at] [{date}] - - (today|tomorrow) + + {amyyear} - - - today or tomorrow? - {day} - - - - - {date} - - {amydate} + ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{eventyear}]|tomorrow|today) + + When? + {date} + - - {datetime} + ref="de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarSpeech.setEvent"> + [create|make] new [{allday}] (event|appointment) [from [the] {start}] [(to|until) [the] {end}] [with {remindertype} [notification]] [[at] {remindertime} [earlier]] [(named|called) {title}] - - {amydatetime} + + * + + + {amyyear} + + + {amyyear} + + + ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{startyear}]|tomorrow|today) + + + ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{endyear}]|tomorrow|today) + + + {amytime} + + + {amytime} + + + {startdate} [at {starttime}] + + + {enddate} [at {endtime}] + + + (all day|yes|true|no|false) + + + * + + + * + + + (email|popup) + + + {amyinteger} + + + (minutes|min|hours|days) + + + {remindertimevalue} {remindertimeunit} - + + How should I name it? + {title} + + + Is it an all day event? + {allday} + + + When does it start? + [at] [the] {start} + + + When does it end? + [at] [the] {end} + + + You can skip each of the following two questions by saying "no" after I asked them. What should I write in the description? + {description} + + + What is the location of the event? + {location} + + + Do you want to receive a notification via email or as popup? + [via|as] {remindertype} + + + How much earlier do you want to be reminded of the event? + {remindertime} + - \ No newline at end of file diff --git a/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-calendar.properties b/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-calendar.properties index 9b79e65a3..f83ff1d9f 100644 --- a/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-calendar.properties +++ b/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-calendar.properties @@ -1 +1,2 @@ -JSON= +CREDENTIALS_JSON= +STORED_CREDENTIALS_PATH=temp/calendarauth \ No newline at end of file diff --git a/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services index c15e8abf7..4ca01d47f 100644 --- a/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services +++ b/plugins/calendar/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -1,2 +1,3 @@ de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarLogic de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarService +de.unistuttgart.iaas.amyassist.amy.plugin.calendar.google.VerificationCodeReceiverService diff --git a/plugins/calendar/src/main/resources/META-INF/javax.ws.rs.Path b/plugins/calendar/src/main/resources/META-INF/javax.ws.rs.Path index c2a3c987d..85f83a9ad 100644 --- a/plugins/calendar/src/main/resources/META-INF/javax.ws.rs.Path +++ b/plugins/calendar/src/main/resources/META-INF/javax.ws.rs.Path @@ -1 +1,2 @@ de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarResource +de.unistuttgart.iaas.amyassist.amy.plugin.calendar.google.VerificationCodeReceiverResource diff --git a/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogicTest.java b/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogicTest.java index 50567af04..69dc8c135 100644 --- a/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogicTest.java +++ b/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarLogicTest.java @@ -23,16 +23,16 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.calendar; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalToIgnoringWhiteSpace; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.stream.Stream; -import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -61,6 +61,9 @@ public class CalendarLogicTest { private CalendarLogic callog; private CalendarService calendarService; + + private Event event1; + private Event event2; /** * Initializes the class variables before each test @@ -70,6 +73,8 @@ public void setup() { this.calendarService = this.framework.mockService(CalendarService.class); this.framework.mockService(Environment.class); this.callog = this.framework.setServiceUnderTest(CalendarLogic.class); + this.event1 = eventAllDay("All Day Event", "2015-05-29", "2015-05-30"); + this.event2 = event("Normal Event", "2015-05-28T15:30:00", "2015-05-29T15:30:00"); } /** @@ -89,7 +94,6 @@ public void testCheckSetEvent(CalendarEvent calendarEvent) { && event.getDescription().equals(calendarEvent.getDescription()) // compare description && ((event.getStart().getDate() != null) == allDay) // compare all day status && checkDates(calendarEvent, event, allDay) // compare start and end date -// && event.getRecurrence().equals(Arrays.asList(calendarEvent.getRecurrence())) // compare recurrence && event.getReminders().equals(calendarEvent.getReminders()); // compare reminders })); } @@ -107,104 +111,6 @@ public static Stream testSetEvent() { "Testing", "home", "test the addEvent code", "popup", 85, "", true)); } - /** - * @param testCase - * a combination of the input variables and the expected outcome - */ - @ParameterizedTest - @MethodSource("testEventsWithDate") - public void testCheckDayWithDate(Pair testCase) { - String checkDay = this.callog.checkDay(LocalDateTime.parse("2015-05-28T09:00:00"), testCase.getLeft(), true); - assertThat(checkDay, equalToIgnoringWhiteSpace(testCase.getRight())); - } - - /** - * @param testCase - * a combination of the input variables and the expected outcome - */ - @ParameterizedTest - @MethodSource("testEventsWithoutDate") - public void testCheckDayWithoutDate(Pair testCase) { - String checkDay = this.callog.checkDay(LocalDateTime.parse("2015-05-28T09:00:00"), testCase.getLeft(), false); - assertThat(checkDay, equalToIgnoringWhiteSpace(testCase.getRight())); - } - - /** - * - * @return the test cases used in the {@link #testCheckDayWithDate(Pair)} test - */ - public static Stream> testEventsWithDate() { - return Stream.of( - Pair.of(event("test event", "2015-05-29T08:00:00", "2015-05-29T09:30:00"), - "test event on the 29th of may at 08:00 until 09:30."), - Pair.of(event("write tests in Java", "2015-05-28T12:59:15", "2015-05-28T20:30:00"), - "write tests in Java on the 28th of may at 12:59 until 20:30."), - Pair.of(event("event start in past", "2015-05-12T13:05:00", "2015-05-29T15:30:00"), - "event start in past since the 12th of may at 13:05 until the 29th of may at 15:30."), - Pair.of(event("event same day start past", "2015-05-28T04:35:02", "2015-05-28T10:15:00"), - "event same day start past on the 28th of may at 04:35 until 10:15."), - Pair.of(event("event end in future", "2015-05-28T07:30:00", "2015-05-29T12:00:00"), - "event end in future since the 28th of may at 07:30 until the 29th of may at 12:00."), - Pair.of(event("event end today", "2015-05-27T22:00:00", "2015-05-28T10:00:00"), - "event end today since the 27th of may at 22:00 until the 28th of may at 10:00."), - Pair.of(event("event start today end tomorrow", "2015-05-28T15:30:00", "2015-05-29T15:30:00"), - "event start today end tomorrow from the 28th of may at 15:30 until the 29th of may at 15:30."), - Pair.of(eventAllDay("event tomorrow all day", "2015-05-29", "2015-05-30"), - "event tomorrow all day on the 29th of may all day long."), - Pair.of(eventAllDay("", "2015-05-29", "2015-05-30"), - "An event without a title on the 29th of may all day long."), - Pair.of(eventAllDay(null, "2015-05-29", "2015-05-30"), - "An event without a title on the 29th of may all day long.")); - } - - /** - * - * @return the test cases used in the {@link #testCheckDayWithoutDate(Pair)} test - */ - public static Stream> testEventsWithoutDate() { - return Stream.of( - Pair.of(event("event later that day", "2015-05-28T14:00:00", "2015-05-28T16:30:00"), - "event later that day from 14:00 until 16:30."), - Pair.of(eventAllDay("event all day", "2015-05-28", "2015-05-29"), "event all day all day long."), - Pair.of(event("event finish today", "2015-05-27T23:00:00", "2015-05-28T10:00:00"), - "event finish today since the 27th of may at 23:00 until 10:00.")); - } - - private static Event event(String summary, String start, String end) { - Event event = new Event(); - event.setSummary(summary); - event.setStart(new EventDateTime().setDateTime(fromISO(start))); - event.setEnd(new EventDateTime().setDateTime(fromISO(end))); - return event; - } - - private static Event eventAllDay(String summary, String start, String end) { - Event event = new Event(); - event.setSummary(summary); - event.setStart(new EventDateTime().setDate(new DateTime(start))); - event.setEnd(new EventDateTime().setDate(new DateTime(end))); - return event; - } - - /** - * @param iso - * date-time without a time-zone - * @return the DateTime of a String with correct time-zone - */ - private static DateTime fromISO(String iso) { - return fromLocalDateTime(LocalDateTime.parse(iso)); - } - - /** - * - * @param localDateTime - * the LocalDateTime which will be converted into EventDateTime - * @return DateTime - */ - private static DateTime fromLocalDateTime(LocalDateTime localDateTime) { - return new DateTime(localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); - } - /** * * @param localDateTime @@ -247,5 +153,80 @@ private static boolean checkDates(CalendarEvent calendarEvent, Event event, bool } return startCorrect && endCorrect; } + + /** + * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarLogic#getDateTime(LocalDateTime, int)}. + */ + @Test + void testGetDateTime() { + assertThat(this.callog.getDateTime(LocalDateTime.of(2018, 9, 20, 12, 00), 0), + is(new DateTime(ZonedDateTime.of(2018, 9, 20, 0, 0, 0, 0, ZoneId.systemDefault()) + .toInstant().toEpochMilli()))); + } + + /** + * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarLogic#isAllDay(Event)}. + */ + @Test + void testIsAllDay() { + assertThat(CalendarLogic.isAllDay(this.event1), is(true)); + assertThat(CalendarLogic.isAllDay(this.event2), is(false)); + } + + /** + * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarLogic#getLocalDateTimeStart(Event)}. + */ + @Test + void testGetLocalDateTimeStart() { + assertThat(this.callog.getLocalDateTimeStart(this.event1), is(LocalDateTime.of(2015, 05, 29, 00, 00))); + assertThat(this.callog.getLocalDateTimeStart(this.event2), is(LocalDateTime.of(2015, 05, 28, 15, 30))); + } + + /** + * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.calendar.CalendarLogic#getLocalDateTimeEnd(Event)}. + */ + @Test + void testGetLocalDateTimeEnd() { + assertThat(this.callog.getLocalDateTimeEnd(this.event1), + is(LocalDateTime.of(2015, 05, 29, 23, 59, 59, 999000000))); + assertThat(this.callog.getLocalDateTimeEnd(this.event2), is(LocalDateTime.of(2015, 05, 29, 15, 30))); + } + + private static Event event(String summary, String start, String end) { + Event event = new Event(); + event.setSummary(summary); + event.setStart(new EventDateTime().setDateTime(fromISO(start))); + event.setEnd(new EventDateTime().setDateTime(fromISO(end))); + return event; + } + + private static Event eventAllDay(String summary, String start, String end) { + Event event = new Event(); + event.setSummary(summary); + event.setStart(new EventDateTime().setDate(new DateTime(start))); + event.setEnd(new EventDateTime().setDate(new DateTime(end))); + return event; + } + + + /** + * @param iso + * date-time without a time-zone + * @return the DateTime of a String with correct time-zone + */ + private static DateTime fromISO(String iso) { + return fromLocalDateTime(LocalDateTime.parse(iso)); + } + + /** + * + * @param localDateTime + * the LocalDateTime which will be converted into EventDateTime + * @return DateTime + */ + private static DateTime fromLocalDateTime(LocalDateTime localDateTime) { + return new DateTime(localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + } + } diff --git a/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeechTest.java b/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeechTest.java index 446ca2451..9d1ff555a 100644 --- a/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeechTest.java +++ b/plugins/calendar/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/calendar/CalendarSpeechTest.java @@ -23,79 +23,471 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.calendar; -import static org.junit.jupiter.api.Assertions.*; - +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.Map; - +import java.util.stream.Stream; import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.ArgumentMatchers.any; +import static org.hamcrest.Matchers.*; import static org.mockito.Mockito.*; -import javax.annotation.meta.When; - +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; /** * Test class for calendar speech - * @author Lars Buttgereit + * @author Lars Buttgereit, Florian Bauer */ -@ExtendWith({ MockitoExtension.class, FrameworkExtension.class }) +@ExtendWith(FrameworkExtension.class) class CalendarSpeechTest { @Reference - private TestFramework testFramework; + private TestFramework framework; + + private Environment environment; - private CalendarLogic calendar; + private CalendarLogic logic; private CalendarSpeech speech; + private CalendarEvent event; + + private List oneEvent; + private List twoEvents; + + private EntityData numberEntity; + private EntityData dateEntity; + private static EntityData alldayEntity; + private static EntityData startdateEntity; + private static EntityData enddateEntity; + private static EntityData locationEntity; + private static EntityData descriptionEntity; + private static EntityData remindertypeEntity; + private static EntityData remindertimevalueEntity; + private static EntityData remindertimeunitEntity; + private static EntityData titleEntity; - @Mock - private EntityData number; - @Mock - private EntityData day; + /** + * + */ @BeforeEach void init() { - this.calendar = this.testFramework.mockService(CalendarLogic.class); - this.speech = this.testFramework.setServiceUnderTest(CalendarSpeech.class); + this.logic = this.framework.mockService(CalendarLogic.class); + this.environment = this.framework.mockService(Environment.class); + this.speech = this.framework.setServiceUnderTest(CalendarSpeech.class); + this.event = new CalendarEvent("ID", LocalDateTime.of(2018, 9, 26, 10, 00), + LocalDateTime.of(2018, 9, 26, 12, 00), "Summary", "", "", false); + this.oneEvent = Arrays.asList(this.event); + this.twoEvents = Arrays.asList(this.event, this.event); + mockEntityData(); + } + + private void mockEntityData() { + this.numberEntity = Mockito.mock(EntityData.class); + this.dateEntity = Mockito.mock(EntityData.class); + alldayEntity = Mockito.mock(EntityData.class); + startdateEntity = Mockito.mock(EntityData.class); + enddateEntity = Mockito.mock(EntityData.class); + locationEntity = Mockito.mock(EntityData.class); + descriptionEntity = Mockito.mock(EntityData.class); + remindertypeEntity = Mockito.mock(EntityData.class); + remindertimevalueEntity = Mockito.mock(EntityData.class); + remindertimeunitEntity = Mockito.mock(EntityData.class); + titleEntity = Mockito.mock(EntityData.class); + } + + /** + * test getEvents() with asking for one event and getting an empty list + */ + @Test + void testGetEventsWithNoEvents() { + Map map = new HashMap<>(); + when(this.numberEntity.getNumber()).thenReturn(1); + map.put("number", this.numberEntity); + String response = this.speech.getEvents(map); + assertThat(response, equalToIgnoringWhiteSpace("No events found.")); + } + + /** + * test getEvents() with asking for one event and getting an event as response + */ + @Test + void testGetEventsWithOneEvent() { + Map map = new HashMap<>(); + when(this.numberEntity.getNumber()).thenReturn(1); + map.put("number", this.numberEntity); + when(this.logic.getEvents(1)).thenReturn(this.oneEvent); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 10, 00)); + String response = this.speech.getEvents(map); + assertThat(response, equalToIgnoringWhiteSpace("You have following upcoming event: Summary from 10:00 am until 12:00 pm.")); + } + + /** + * test getEvents() with asking for two event and getting two events as response + */ + @Test + void testGetEventsWithTwoEvents() { + Map map = new HashMap<>(); + when(this.numberEntity.getNumber()).thenReturn(2); + map.put("number", this.numberEntity); + when(this.logic.getEvents(2)).thenReturn(this.twoEvents); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 10, 00)); + String response = this.speech.getEvents(map); + assertThat(response, equalToIgnoringWhiteSpace("You have following upcoming 2 events: Summary from 10:00 am " + + "until 12:00 pm. Summary from 10:00 am until 12:00 pm.")); } + /** + * test getEventsAt() with today and getting an empty list + */ @Test - void testGetEvents() { + void testGetEventsTodayWithNoEvent() { Map map = new HashMap<>(); - when(this.number.getNumber()).thenReturn(1); - map.put("number", number); - this.speech.getEvents(map); - verify(this.calendar).getEvents(1); + when(this.dateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 20)); + when(this.dateEntity.getString()).thenReturn("today"); + map.put("date", this.dateEntity); + map.put("eventyear", null); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 10, 00)); + String response = this.speech.getEventsAt(map); + assertThat(response, equalToIgnoringWhiteSpace("You have no events today.")); } + /** + * test getEventsAt() with today and getting one event as response + */ @Test - void testGetEventsToday() { + void testGetEventsTodayWithOneEvent() { Map map = new HashMap<>(); - when(this.day.getString()).thenReturn("today"); - map.put("day", this.day); - this.speech.getEventsToday(map); - verify(this.calendar).getEventsToday(); + when(this.dateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 26)); + when(this.dateEntity.getString()).thenReturn("today"); + map.put("date", this.dateEntity); + map.put("eventyear", null); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 26, 8, 00)); + when(this.logic.getEventsAt(LocalDateTime.of(2018, 9, 26, 00, 00))).thenReturn(this.oneEvent); + String response = this.speech.getEventsAt(map); + assertThat(response, equalToIgnoringWhiteSpace("You have following events today: Summary from 10:00 am until 12:00 pm.")); } + /** + * test getEventsAt() with tomorrow and getting an empty list + */ @Test void testGetEventsTomorrow() { Map map = new HashMap<>(); - when(this.day.getString()).thenReturn("tomorrow"); - map.put("day", this.day); - this.speech.getEventsToday(map); - verify(this.calendar).getEventsTomorrow(); + when(this.dateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 21)); + when(this.dateEntity.getString()).thenReturn("tomorrow"); + map.put("date", this.dateEntity); + map.put("eventyear", null); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 10, 00)); + String response = this.speech.getEventsAt(map); + assertThat(response, equalToIgnoringWhiteSpace("You have no events tomorrow.")); } + + /** + * test getEventsAt() with 20/9/2018 and getting an empty list + */ + @Test + void testGetEventsAt() { + Map map = new HashMap<>(); + when(this.dateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 20)); + when(this.dateEntity.getString()).thenReturn(""); + map.put("date", this.dateEntity); + map.put("eventyear", null); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 21, 10, 00)); + String response = this.speech.getEventsAt(map); + assertThat(response, equalToIgnoringWhiteSpace("No events found for the 20th of september 2019.")); + } + + /** + * test getEventsToday() with 20/9/2018 and getting one event as response + */ + @Test + void testGetEventsAtWithOneEvent() { + Map map = new HashMap<>(); + when(this.dateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 20)); + when(this.dateEntity.getString()).thenReturn(""); + map.put("date", this.dateEntity); + map.put("eventyear", null); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 10, 00)); + when(this.logic.getEventsAt(LocalDateTime.of(2018, 9, 20, 00, 00))).thenReturn(this.oneEvent); + String response = this.speech.getEventsAt(map); + assertThat(response, equalToIgnoringWhiteSpace("You have following events on the 20th of september 2018: " + + "Summary from 10:00 am until 12:00 pm.")); +} + + /** + * Test method for setEvent(Map) + */ + @Test + void testSetEvent() { + Map map = new HashMap<>(); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 00, 00)); + when(alldayEntity.getString()).thenReturn("yes"); + map.put("allday", alldayEntity); + when(startdateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 16)); + map.put("startdate", startdateEntity); + when(enddateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 19)); + map.put("enddate", enddateEntity); + map.put("startyear", null); + map.put("endyear", null); + when(locationEntity.getString()).thenReturn("no"); + map.put("location", locationEntity); + when(descriptionEntity.getString()).thenReturn("no"); + map.put("description", descriptionEntity); + when(remindertimevalueEntity.getNumber()).thenReturn(20); + map.put("remindertimevalue", remindertimevalueEntity); + when(remindertimeunitEntity.getString()).thenReturn("days"); + map.put("remindertimeunit", remindertimeunitEntity); + when(titleEntity.getString()).thenReturn("Okay"); + map.put("title", titleEntity); + when(remindertypeEntity.getString()).thenReturn("email"); + map.put("remindertype", remindertypeEntity); + CalendarEvent event2 = new CalendarEvent(LocalDateTime.of(2019, 9, 17, 00, 00), + LocalDateTime.of(2019, 9, 21, 00, 00), "Okay", "", "", "email", 28800, "", true); + this.speech.setEvent(map); + verify(this.logic).setEvent(event2); + } + + /** + * Test method for setEvent(Map) + */ + @Test + void testSetEvent2() { + Map map = new HashMap<>(); + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 00, 00)); + when(alldayEntity.getString()).thenReturn("yes"); + map.put("allday", alldayEntity); + when(startdateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 16)); + map.put("startdate", startdateEntity); + when(enddateEntity.getDate()).thenReturn(LocalDate.of(2018, 9, 19)); + map.put("enddate", enddateEntity); + map.put("startyear", null); + map.put("endyear", null); + when(locationEntity.getString()).thenReturn("location"); + map.put("location", locationEntity); + when(descriptionEntity.getString()).thenReturn("description"); + map.put("description", descriptionEntity); + when(remindertimevalueEntity.getNumber()).thenReturn(20); + map.put("remindertimevalue", remindertimevalueEntity); + when(remindertimeunitEntity.getString()).thenReturn("hours"); + map.put("remindertimeunit", remindertimeunitEntity); + when(titleEntity.getString()).thenReturn("Okay"); + map.put("title", titleEntity); + when(remindertypeEntity.getString()).thenReturn("email"); + map.put("remindertype", remindertypeEntity); + CalendarEvent event2 = new CalendarEvent(LocalDateTime.of(2019, 9, 17, 00, 00), + LocalDateTime.of(2019, 9, 21, 00, 00), "Okay", "location", "description", "email", 1200, "", true); + this.speech.setEvent(map); + verify(this.logic).setEvent(event2); + } + + /** + * @param testCase + * a combination of the input variables and the expected outcome + */ + @ParameterizedTest + @MethodSource("testNewEvents") + public void testSetEvent(Pair, String> testCase) { + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 00, 00)); + String setEvent = this.speech.setEvent(testCase.getLeft()); + assertThat(setEvent, equalToIgnoringWhiteSpace(testCase.getRight())); + } + + /** + * + * @return the test cases used in the {@link #testSetEvent(Pair)} test + */ + public static Stream, String>> testNewEvents() { + return Stream.of(Pair.of(speechMap("no", LocalDate.of(2018, 9, 21), LocalDate.of(2018, 9, 21), "", "", null, null), + "You have to restart the creation of a new event and please make sure that you add a time to the start" + + " and to the end if you choose an non all day event."), + Pair.of(speechMap("no", LocalDate.of(2018, 9, 16), LocalDate.of(2018, 9, 16), "", "2018", LocalTime.of(12, 00), null), + "You have to restart the creation of a new event and please make sure that you add a time to the start" + + " and to the end if you choose an non all day event."), + Pair.of(speechMap("false", LocalDate.of(2018, 9, 16), LocalDate.of(2018, 9, 16), "2018", "", LocalTime.of(12, 01), LocalTime.of(12, 00)), + "You have to restart the creation of a new event and please make sure that the start of the event " + + "is before the end."), + Pair.of(speechMap("yes", LocalDate.of(2018, 9, 21), LocalDate.of(2018, 9, 15), "", "", LocalTime.of(12, 00), LocalTime.of(12, 00)), + "You have to restart the creation of a new event and please make sure that the start of the event " + + "is before the end."), + Pair.of(speechMap("true", LocalDate.of(2018, 9, 21), LocalDate.of(2018, 9, 15), "2018", "", LocalTime.of(12, 00), LocalTime.of(12, 00)), + "You have to restart the creation of a new event and please make sure that the start of the event " + + "is before the end."), + Pair.of(speechMap("all day", LocalDate.of(2018, 9, 21), LocalDate.of(2018, 9, 15), "", "2018", LocalTime.of(12, 00), LocalTime.of(12, 00)), + "You have to restart the creation of a new event and please make sure that the start of the event " + + "is before the end.")); + } + + private static Map speechMap(String allDay, LocalDate startDate, LocalDate endDate, String startYear, + String endYear, LocalTime startTime, LocalTime endTime) { + EntityData allday = Mockito.mock(EntityData.class); + EntityData startdate = Mockito.mock(EntityData.class); + EntityData startyear = Mockito.mock(EntityData.class); + EntityData starttime = Mockito.mock(EntityData.class); + EntityData enddate = Mockito.mock(EntityData.class); + EntityData endyear = Mockito.mock(EntityData.class); + EntityData endtime = Mockito.mock(EntityData.class); + EntityData location = Mockito.mock(EntityData.class); + EntityData description = Mockito.mock(EntityData.class); + EntityData remindertimetalue = Mockito.mock(EntityData.class); + EntityData remindertimeunit = Mockito.mock(EntityData.class); + EntityData title = Mockito.mock(EntityData.class); + EntityData remindertype = Mockito.mock(EntityData.class); + Map map = new HashMap<>(); + when(allday.getString()).thenReturn(allDay); + map.put("allday", allday); + when(startdate.getDate()).thenReturn(startDate); + map.put("startdate", startdate); + when(enddate.getDate()).thenReturn(endDate); + map.put("enddate", enddate); + if (startYear.isEmpty()) { + map.put("startyear", null); + } else { + map.put("startyear", startyear); + } + if (endYear.isEmpty()) { + map.put("endyear", null); + } else { + map.put("endyear", endyear); + } + if (startTime == null) { + map.put("starttime", null); + } else { + when(starttime.getTime()).thenReturn(startTime); + map.put("starttime", starttime); + } + if (endTime == null) { + map.put("endtime", null); + } else { + when(endtime.getTime()).thenReturn(endTime); + map.put("endtime", endtime); + } + when(location.getString()).thenReturn("test"); + map.put("location", location); + when(description.getString()).thenReturn("test"); + map.put("description", description); + when(remindertimetalue.getNumber()).thenReturn(20); + map.put("remindertimevalue", remindertimetalue); + when(remindertimeunit.getString()).thenReturn("days"); + map.put("remindertimeunit", remindertimeunit); + when(title.getString()).thenReturn("Okay"); + map.put("title", title); + when(remindertype.getString()).thenReturn("email"); + map.put("remindertype", remindertype); + return map; + } + + /** + * @param testCase + * a combination of the input variables and the expected outcome + */ + @ParameterizedTest + @MethodSource("testEventsWithDate") + public void testCheckDayWithDate(Pair testCase) { + String checkDay = this.speech.checkDate(LocalDateTime.parse("2015-05-28T09:00:00"), testCase.getLeft(), true); + assertThat(checkDay, equalToIgnoringWhiteSpace(testCase.getRight())); + } + + /** + * @param testCase + * a combination of the input variables and the expected outcome + */ + @ParameterizedTest + @MethodSource("testEventsWithoutDate") + public void testCheckDayWithoutDate(Pair testCase) { + String checkDay = this.speech.checkDate(LocalDateTime.parse("2015-05-28T09:00:00"), testCase.getLeft(), false); + assertThat(checkDay, equalToIgnoringWhiteSpace(testCase.getRight())); + } + + /** + * + * @return the test cases used in the {@link #testCheckDayWithDate(Pair)} test + */ + public static Stream> testEventsWithDate() { + return Stream.of( + Pair.of(event("test event", "2015-05-29T08:00:00", "2015-05-29T09:30:00"), + "test event on the 29th of may at 08:00 am until 09:30 am."), + Pair.of(event("write tests in Java", "2015-05-28T12:59:15", "2015-05-28T20:30:00"), + "write tests in Java on the 28th of may at 12:59 pm until 08:30 pm."), + Pair.of(event("event start in past", "2015-05-12T13:05:00", "2015-05-29T15:30:00"), + "event start in past since the 12th of may at 01:05 pm until the 29th of may at 03:30 pm."), + Pair.of(event("event same day start past", "2015-05-28T04:35:02", "2015-05-28T10:15:00"), + "event same day start past on the 28th of may at 04:35 am until 10:15 am."), + Pair.of(event("event end in future", "2015-05-28T07:30:00", "2015-05-29T12:00:00"), + "event end in future since the 28th of may at 07:30 am until the 29th of may at 12:00 pm."), + Pair.of(event("event end today", "2015-05-27T22:00:00", "2015-05-28T10:00:00"), + "event end today since the 27th of may at 10:00 pm until the 28th of may at 10:00 am."), + Pair.of(event("event start today end tomorrow", "2015-05-28T15:30:00", "2015-05-29T15:30:00"), + "event start today end tomorrow from the 28th of may at 03:30 pm until the 29th of may at 03:30 pm."), + Pair.of(eventAllDay("event tomorrow all day", "2015-05-29", "2015-05-29"), + "event tomorrow all day on the 29th of may all day long."), + Pair.of(eventAllDay("", "2015-05-29", "2015-05-29"), + "An event without a title on the 29th of may all day long."), + Pair.of(eventAllDay("", "2015-05-29", "2015-05-29"), + "An event without a title on the 29th of may all day long.")); + } + + /** + * + * @return the test cases used in the {@link #testCheckDayWithoutDate(Pair)} test + */ + public static Stream> testEventsWithoutDate() { + return Stream.of( + Pair.of(event("event later that day", "2015-05-28T14:00:00", "2015-05-28T16:30:00"), + "event later that day from 02:00 pm until 04:30 pm."), + Pair.of(eventAllDay("event all day", "2015-05-28", "2015-05-28"), "event all day all day long."), + Pair.of(event("event finish today", "2015-05-27T23:00:00", "2015-05-28T10:00:00"), + "event finish today since the 27th of may at 11:00 pm until 10:00 am.")); + } + + private static CalendarEvent event(String summary, String start, String end) { + return new CalendarEvent("", fromStart(start), fromEnd(end), summary, "", "", false); + } + + private static CalendarEvent eventAllDay(String summary, String start, String end) { + return new CalendarEvent("", fromStart(start), fromEnd(end), summary, "", "", true); + } + + /** + * @param input + * date string + * @return the LocalDateTime of a String + */ + private static LocalDateTime fromStart(String input) { + if (input.contains("T")) { + return LocalDateTime.parse(input); + } + String modifiedInput = input + "T00:00"; + return LocalDateTime.parse(modifiedInput); + } + + /** + * @param input + * date string + * @return the LocalDateTime of a String + */ + private static LocalDateTime fromEnd(String input) { + if (input.contains("T")) { + return LocalDateTime.parse(input); + } + String modifiedInput = input + "T23:59"; + return LocalDateTime.parse(modifiedInput); + } + } diff --git a/plugins/email/pom.xml b/plugins/email/pom.xml index 4fa2278ab..98179d391 100644 --- a/plugins/email/pom.xml +++ b/plugins/email/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailLogic.java b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailLogic.java index d02c43964..ffa7d64ce 100644 --- a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailLogic.java +++ b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailLogic.java @@ -23,18 +23,21 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.email; -import java.io.IOException; import java.time.LocalDateTime; import java.time.ZoneId; import java.util.*; -import javax.mail.*; +import javax.mail.Flags; import javax.mail.Flags.Flag; +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; import javax.mail.search.FlagTerm; import org.slf4j.Logger; +import de.unistuttgart.iaas.amyassist.amy.core.configuration.WithDefault; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; @@ -59,8 +62,9 @@ public class EMailLogic { @Reference private ContactRegistry contactRegistry; + @WithDefault @Reference - private Properties configLoader; + private Properties properties; @Reference private IStorage storage; @@ -94,6 +98,9 @@ public class EMailLogic { * look for new mails * * @return true, if messages have been found, else false + * + * @throws IllegalStateException + * when mail session is not connected */ public boolean hasNewMessages(boolean checkForImportant) { if (checkForImportant) { @@ -109,6 +116,9 @@ public boolean hasNewMessages(boolean checkForImportant) { * put true here if you want the amount of new important mails * * @return number of messages in inbox + * + * @throws IllegalStateException + * when mail session is not connected */ public int getNewMessageCount(boolean checkForImportant) { if (checkForImportant) { @@ -127,6 +137,9 @@ public int getNewMessageCount(boolean checkForImportant) { * set this to true, if you only want the important mails to be converted, set this to false if you want * every message to be converted * @return readable String of new messages + * + * @throws IllegalStateException + * when mail session is not connected */ public String printMessages(int amount, boolean important) { List messagesToPrint; @@ -148,12 +161,9 @@ public String printMessages(int amount, boolean important) { Message m = messagesToPrint.get(i); sb.append(getFrom(m) + "\n"); sb.append(m.getSubject() + "\n"); - sb.append(getContentFromMessage(m) + "\n\n"); } } catch (MessagingException me) { this.logger.error("Operations on the message failed", me); - } catch (IOException ioe) { - this.logger.error("Getting content from message failed", ioe); } return sb.toString(); } @@ -165,6 +175,9 @@ public String printMessages(int amount, boolean important) { * Get all unread messages from the inbox * * @return all unread messages in inbox, from newest to oldest + * + * @throws IllegalStateException + * when mail session is not connected */ List getNewMessages() { try { @@ -182,6 +195,9 @@ List getNewMessages() { * Get all important messages from given message array * * @return all important messages in given message array + * + * @throws IllegalStateException + * when mail session is not connected */ List getNewImportantMessages() { List unseenMessages = getNewMessages(); @@ -199,47 +215,6 @@ List getNewImportantMessages() { } } - /** - * Get content in the message - * - * @param message - * the message - * @return content of the message as readable String - * @throws MessagingException - * if something went wrong - * @throws IOException - * if something went wrong - */ - String getContentFromMessage(Message message) throws MessagingException, IOException { - if (message.isMimeType("text/plain")) { - return message.getContent().toString(); - } else if (message.isMimeType("multipart/*")) { - StringBuilder sb = new StringBuilder(); - try { - Multipart mp = (Multipart) message.getContent(); - for (int i = 0; i < mp.getCount(); i++) { - Part part = mp.getBodyPart(i); - if (part.isMimeType("text/plain")) { - sb.append(i + part.getContent().toString() + "\n"); - } else { - sb.append(i + "Body part is not plain text\n"); - } - } - } catch (ClassCastException cce) { - /* - * Normally, casting should work fine when the content type is multipart, but there seems to be a - * problem with finding the mail configuration, for more info, see: - * https://javaee.github.io/javamail/FAQ#castmultipart - */ - this.logger.debug("Still getting a stream back", cce); - sb.append("Cannot read message content"); - } - return sb.toString(); - } - return "Message content not readable"; - - } - /** * Get all mails in the inbox and convert them to MessageDTO objects for the REST class to send to the web app * @@ -247,6 +222,9 @@ String getContentFromMessage(Message message) throws MessagingException, IOExcep * the amount of mails, put -1 here if you want all mails * * @return array with the requested mails, from newest to oldest, empty array if there was an error + * + * @throws IllegalStateException + * when mail session is not connected */ public MessageDTO[] getMailsForREST(int amount) { Message[] messages; @@ -261,11 +239,11 @@ public MessageDTO[] getMailsForREST(int amount) { // we switch order because we get the messages from the inbox from oldest to newest Message m = messages[messages.length - 1 - i]; LocalDateTime sentDate = m.getSentDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime(); - messagesToSend[i] = new MessageDTO(getFrom(m), m.getSubject(), getContentFromMessage(m), sentDate, - isImportantMessage(m), m.isSet(Flag.SEEN)); + messagesToSend[i] = new MessageDTO(getFrom(m), m.getSubject(), sentDate, isImportantMessage(m), + m.isSet(Flag.SEEN)); } return messagesToSend; - } catch (MessagingException | IOException e) { + } catch (MessagingException e) { this.logger.error("There were problems handling the messages", e); return new MessageDTO[0]; } @@ -314,8 +292,8 @@ boolean isImportantMessage(Message message) throws MessagingException { public boolean connectToMailServer(EMailCredentials credentials) { if (credentials == null) { // if no credentials are given, use the standard ones for Amy - String amyUsername = this.configLoader.getProperty(AMY_MAIL_ADDRESS_KEY); - String amyPassword = this.configLoader.getProperty(AMY_MAIL_PW_KEY); + String amyUsername = this.properties.getProperty(AMY_MAIL_ADDRESS_KEY); + String amyPassword = this.properties.getProperty(AMY_MAIL_PW_KEY); String amyHost = AMY_MAIL_HOST; credentials = new EMailCredentials(amyUsername, amyPassword, amyHost); diff --git a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailSpeech.java b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailSpeech.java index 49eacad47..d145cf9b0 100644 --- a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailSpeech.java +++ b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailSpeech.java @@ -85,14 +85,18 @@ public String disconnect(Map entities) { */ @Intent() public String newMessages(Map entities) { - if (entities.get(IMPORTANT) != null && entities.get(IMPORTANT).getString().contains(IMPORTANT)) { - if (this.logic.hasNewMessages(true)) { - return "You have new important messages."; + try { + if (entities.get(IMPORTANT) != null && entities.get(IMPORTANT).getString().contains(IMPORTANT)) { + if (this.logic.hasNewMessages(true)) { + return "You have new important messages."; + } + return NO_IMP_MAILS; } - return NO_IMP_MAILS; - } - if (this.logic.hasNewMessages(false)) { - return "You have new messages."; + if (this.logic.hasNewMessages(false)) { + return "You have new messages."; + } + } catch (IllegalStateException ise) { + return ise.getMessage(); } return NO_MAILS; } @@ -106,18 +110,22 @@ public String newMessages(Map entities) { */ @Intent() public String numberOfNewMails(Map entities) { - if (entities.get(IMPORTANT) != null && entities.get(IMPORTANT).getString().contains(IMPORTANT)) { - int count = this.logic.getNewMessageCount(true); + try { + if (entities.get(IMPORTANT) != null && entities.get(IMPORTANT).getString().contains(IMPORTANT)) { + int count = this.logic.getNewMessageCount(true); + if (count > 0) { + return count + " new important messages"; + } + return NO_IMP_MAILS; + } + int count = this.logic.getNewMessageCount(false); if (count > 0) { - return count + " new important messages"; + return count + " new mails."; } - return NO_IMP_MAILS; + return NO_MAILS; + } catch (IllegalStateException ise) { + return ise.getMessage(); } - int count = this.logic.getNewMessageCount(false); - if (count > 0) { - return count + " new mails."; - } - return NO_MAILS; } /** @@ -133,18 +141,22 @@ public String readRecentMails(Map entities) { if (entities.get(IMPORTANT) != null) { important = entities.get(IMPORTANT).getString().contains(IMPORTANT); } - if (entities.get("all") != null && entities.get("all").getString() != null) { - if (important) { - return this.logic.printMessages(-1, true); + try { + if (entities.get("all") != null && entities.get("all").getString() != null) { + if (important) { + return this.logic.printMessages(-1, true); + } + return this.logic.printMessages(-1, false); } - return this.logic.printMessages(-1, false); - } - if (entities.get("number") != null) { - int amount = entities.get("number").getNumber(); - if (important) { - return this.logic.printMessages(amount, true); + if (entities.get("number") != null) { + int amount = entities.get("number").getNumber(); + if (important) { + return this.logic.printMessages(amount, true); + } + return this.logic.printMessages(amount, false); } - return this.logic.printMessages(amount, false); + } catch (IllegalStateException ise) { + return ise.getMessage(); } return ""; } diff --git a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/MailUpdateService.java b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/MailUpdateService.java new file mode 100644 index 000000000..d2d3bdd91 --- /dev/null +++ b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/MailUpdateService.java @@ -0,0 +1,108 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.email; + +import java.util.List; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import javax.mail.Message; +import javax.mail.MessagingException; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService; +import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; + +/** + * This class checks every few minutes if the user has received a new email + * + * @author Patrick Singer + */ +@Service(MailUpdateService.class) +public class MailUpdateService implements RunnableService { + + @Reference + private EMailLogic mailLogic; + + @Reference + private MessageHub messageHub; + + @Reference + private TaskScheduler scheduler; + + @Reference + private Logger logger; + + private ScheduledFuture nextScheduledCall; + + private int lastMessageCount; + + private static final int MINUTE_INTERVAL = 1; + + private void checkForNewMails() { + if (this.mailLogic.isConnectedToMailServer()) { + final List messages = this.mailLogic.getNewMessages(); + final int currentMessageCount = messages.size(); + if (currentMessageCount > this.lastMessageCount) { + final Message newest = messages.get(0); + try { + final String senderInfo = EMailLogic.getFrom(newest); + final String subjectInfo = newest.getSubject(); + this.messageHub.publish("user/all/notification", + "You've got new mail from " + senderInfo + ".\nSubject: " + subjectInfo); + } catch (MessagingException me) { + this.logger.error("Getting info from message failed", me); + } + } + this.lastMessageCount = currentMessageCount; + } + scheduleNextUpdate(); + } + + private void scheduleNextUpdate() { + this.nextScheduledCall = this.scheduler.schedule(this::checkForNewMails, MINUTE_INTERVAL, TimeUnit.MINUTES); + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#start() + */ + @Override + public void start() { + scheduleNextUpdate(); + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#stop() + */ + @Override + public void stop() { + if (this.nextScheduledCall != null) { + this.nextScheduledCall.cancel(true); + } + } +} diff --git a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/rest/MessageDTO.java b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/rest/MessageDTO.java index 5692c2bfc..10df8c58e 100644 --- a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/rest/MessageDTO.java +++ b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/rest/MessageDTO.java @@ -41,7 +41,6 @@ public class MessageDTO extends Entity { private String from; private String subject; - private String content; @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) private LocalDateTime sentDate; @@ -62,8 +61,6 @@ public MessageDTO() { * the mail address of the sender * @param subject * subject of the messages - * @param content - * the content of the message * @param sentDate * the time the mail was sent * @param important @@ -71,11 +68,9 @@ public MessageDTO() { * @param seen * is message seen */ - public MessageDTO(String from, String subject, String content, LocalDateTime sentDate, boolean important, - boolean seen) { + public MessageDTO(String from, String subject, LocalDateTime sentDate, boolean important, boolean seen) { this.from = from; this.subject = subject; - this.content = content; this.sentDate = sentDate; this.important = important; this.seen = seen; @@ -95,13 +90,6 @@ public String getSubject() { return this.subject; } - /** - * @return return content of message - */ - public String getContent() { - return this.content; - } - /** * @return date when the message was sent */ diff --git a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/session/MailSession.java b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/session/MailSession.java index 4b82b62d1..4db904038 100644 --- a/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/session/MailSession.java +++ b/plugins/email/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/session/MailSession.java @@ -60,8 +60,14 @@ public class MailSession { * @return inbox folder * @throws MessagingException * if folder couldn't be found or couldn't be opened + * + * @throws IllegalStateException + * if session is not connected */ public Folder getInbox() throws MessagingException { + if (!this.connected) { + throw new IllegalStateException("Session is not connected"); + } Folder folderToOpen = this.store.getFolder(INBOX_NAME); folderToOpen.open(Folder.READ_ONLY); return folderToOpen; diff --git a/plugins/email/src/main/resources/EmailSpeech.aim.xml b/plugins/email/src/main/resources/EmailSpeech.aim.xml index efca77316..852a42865 100644 --- a/plugins/email/src/main/resources/EmailSpeech.aim.xml +++ b/plugins/email/src/main/resources/EmailSpeech.aim.xml @@ -2,24 +2,24 @@ - [do i have] new [{important}] (messages|mails) + [do i have|are there] [any] new [{important}] (messages|mails|miles) - important + important - connect to amy mail + connect to [amy] mail - disconnect from mail + disconnect from [amy] mail - how many [new] [{important}] (messages|mails) [do i have] + how many [new] [{important}] (messages|mails|miles) [do i have] important @@ -28,7 +28,7 @@ - read ({selection}) [{important}] (messages|mails) + (read|get) ({selection}) [{important}] (messages|mails) important @@ -39,11 +39,9 @@ all - ({all}|{number}) - \ No newline at end of file diff --git a/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-email.properties b/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-email.properties new file mode 100644 index 000000000..9e25edba5 --- /dev/null +++ b/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-email.properties @@ -0,0 +1,3 @@ +#Email fallback properties +email_usr= +email_pw= \ No newline at end of file diff --git a/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services index 3bfeee3cb..e28ba81f4 100644 --- a/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services +++ b/plugins/email/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -1,2 +1,3 @@ de.unistuttgart.iaas.amyassist.amy.plugin.email.EMailLogic de.unistuttgart.iaas.amyassist.amy.plugin.email.session.MailSession +de.unistuttgart.iaas.amyassist.amy.plugin.email.MailUpdateService diff --git a/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailRestTest.java b/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailRestTest.java index 04e1e97ec..694115e5b 100644 --- a/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailRestTest.java +++ b/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EMailRestTest.java @@ -119,7 +119,6 @@ public void testGetMailsAll() { assertThat(message1.getFrom(), equalTo(message2.getFrom())); assertThat(message1.getSubject(), equalTo(message2.getSubject())); - assertThat(message1.getContent(), equalTo(message2.getContent())); assertThat(message1.getSentDate(), equalTo(message2.getSentDate())); assertThat(message1.isImportant(), equalTo(message2.isImportant())); } @@ -147,7 +146,6 @@ public void testGetMailsAmount() { assertThat(message1.getFrom(), equalTo(message2.getFrom())); assertThat(message1.getSubject(), equalTo(message2.getSubject())); - assertThat(message1.getContent(), equalTo(message2.getContent())); assertThat(message1.getSentDate(), equalTo(message2.getSentDate())); assertThat(message1.isImportant(), equalTo(message2.isImportant())); } @@ -172,8 +170,8 @@ private MessageDTO[] createMessages(int amount) { byte[] bytes = new byte[5]; random.nextBytes(bytes); String randomString = new String(bytes); - messages[i] = new MessageDTO(randomString, randomString, randomString, LocalDateTime.now(), - random.nextBoolean(), random.nextBoolean()); + messages[i] = new MessageDTO(randomString, randomString, LocalDateTime.now(), random.nextBoolean(), + random.nextBoolean()); } return messages; } diff --git a/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EmailLogicTest.java b/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EmailLogicTest.java index 2b6b7bce7..618d5fb12 100644 --- a/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EmailLogicTest.java +++ b/plugins/email/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/email/EmailLogicTest.java @@ -35,8 +35,10 @@ import java.time.ZoneId; import java.util.*; -import javax.mail.*; import javax.mail.Flags.Flag; +import javax.mail.Folder; +import javax.mail.Message; +import javax.mail.MessagingException; import javax.mail.internet.InternetAddress; import javax.mail.search.FlagTerm; @@ -71,10 +73,10 @@ public class EmailLogicTest { private ContactRegistry contactRegistry; - private Properties properties; - private EMailLogic emailLogic; + private Properties properties; + private Folder inboxMock; private List contacts = new ArrayList<>(); @@ -221,11 +223,9 @@ public void testPrintMessagesImportant() throws MessagingException, IOException if (mailAddress.contains("important")) { assertThat(allMails, containsString(mailAddress)); assertThat(allMails, containsString(m.getSubject())); - assertThat(allMails, containsString(m.getContent().toString())); } else { assertThat(allMails, not(containsString(mailAddress))); assertThat(allMails, not(containsString(m.getSubject()))); - assertThat(allMails, not(containsString(m.getContent().toString()))); } } @@ -237,11 +237,9 @@ public void testPrintMessagesImportant() throws MessagingException, IOException if (mailAddress.contains("important") && i < amount) { assertThat(someMails, containsString(mailAddress)); assertThat(someMails, containsString(m.getSubject())); - assertThat(someMails, containsString(m.getContent().toString())); } else { assertThat(someMails, not(containsString(mailAddress))); assertThat(someMails, not(containsString(m.getSubject()))); - assertThat(someMails, not(containsString(m.getContent().toString()))); } } } @@ -274,7 +272,6 @@ public void testPrintMessagesNotImportant() throws MessagingException, IOExcepti String mailAddress = ((InternetAddress) m.getFrom()[0]).getAddress(); assertThat(allMails, containsString(mailAddress)); assertThat(allMails, containsString(m.getSubject())); - assertThat(allMails, containsString(m.getContent().toString())); } // check someMails @@ -285,11 +282,9 @@ public void testPrintMessagesNotImportant() throws MessagingException, IOExcepti if (i < amount) { assertThat(someMails, containsString(mailAddress)); assertThat(someMails, containsString(m.getSubject())); - assertThat(someMails, containsString(m.getContent().toString())); } else { assertThat(someMails, not(containsString(mailAddress))); assertThat(someMails, not(containsString(m.getSubject()))); - assertThat(someMails, not(containsString(m.getContent().toString()))); } } } @@ -353,73 +348,6 @@ public void testGetNewImportantMessagesNoMessages() throws MessagingException, I assertThat(returnedMessages, is(Collections.emptyList())); } - /** - * Test method for {@link EMailLogic#getContentFromMessage(Message)} - * - * @throws IOException - * if something goes wrong - * @throws MessagingException - * if something goes wrong - */ - @Test - public void testGetContentFromMessagePlainText() throws IOException, MessagingException { - Message[] messages = createMockMessages(10, 5); - for (Message m : messages) { - assertThat(this.emailLogic.getContentFromMessage(m), containsString("content")); - } - } - - /** - * Test method for {@link EMailLogic#getContentFromMessage(Message)} - * - * @throws IOException - * if something goes wrong - * @throws MessagingException - * if something goes wrong - */ - @Test - public void testGetContentFromMessageMultipart() throws IOException, MessagingException { - Message[] messages = createMockMessages(10, 5); - for (int i = 0; i < messages.length; i++) { - // setting up the mocks - Message m = messages[i]; - when(m.isMimeType("text/plain")).thenReturn(false); - when(m.isMimeType("multipart/*")).thenReturn(true); - Multipart mp = mock(Multipart.class); - when(m.getContent()).thenReturn(mp); - when(mp.getCount()).thenReturn(2); - BodyPart part1 = mock(BodyPart.class); - BodyPart part2 = mock(BodyPart.class); - when(mp.getBodyPart(0)).thenReturn(part1); - when(mp.getBodyPart(1)).thenReturn(part2); - when(part1.isMimeType("text/plain")).thenReturn(true); - when(part1.getContent()).thenReturn("innerContent" + i); - - // testing - String resultString = this.emailLogic.getContentFromMessage(m); - assertThat(resultString, containsString("innerContent")); - assertThat(resultString, containsString("Body part is not plain text")); - } - - } - - /** - * Test method for {@link EMailLogic#getContentFromMessage(Message)} - * - * @throws IOException - * if something goes wrong - * @throws MessagingException - * if something goes wrong - */ - @Test - public void testGetContentFromMessageUnreadable() throws IOException, MessagingException { - Message[] messages = createMockMessages(10, 5); - for (Message m : messages) { - when(m.isMimeType("text/plain")).thenReturn(false); - assertThat(this.emailLogic.getContentFromMessage(m), containsString("Message content not readable")); - } - } - /** * Tests {@link EMailLogic#getMailsForREST(int)} * @@ -459,12 +387,12 @@ public void testGetMailsForRESTAmount() throws MessagingException, IOException { } } - private void compareMessages(Message message, MessageDTO transferMessage) throws MessagingException, IOException { + private void compareMessages(Message message, MessageDTO transferMessage) throws MessagingException { String messageFrom = ((InternetAddress) message.getFrom()[0]).getAddress(); assertThat(transferMessage.getFrom(), is(messageFrom)); assertThat(transferMessage.getSubject(), is(message.getSubject())); - assertThat(transferMessage.getContent(), is(message.getContent().toString())); + // need to convert from Date to LocalDateTime assertThat(transferMessage.getSentDate(), is(message.getSentDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime())); @@ -601,13 +529,12 @@ protected void testGetFrom() throws MessagingException, IOException { assertThat(EMailLogic.getFrom(messages[1]), containsString("address")); } - private Message[] createMockMessages(int amount, int amountImportant) throws MessagingException, IOException { + private Message[] createMockMessages(int amount, int amountImportant) throws MessagingException { Message[] messages = new Message[amount]; for (int i = 0; i < amount; i++) { Message mockMessage = mock(Message.class); when(mockMessage.getSubject()).thenReturn("subject" + i); when(mockMessage.isMimeType("text/plain")).thenReturn(true); - when(mockMessage.getContent()).thenReturn("content" + i); InternetAddress address = mock(InternetAddress.class); when(mockMessage.getFrom()).thenReturn(new InternetAddress[] { address }); when(mockMessage.getSentDate()).thenReturn(new Date(System.currentTimeMillis())); diff --git a/plugins/example/pom.xml b/plugins/example/pom.xml index e60ffc8c7..429995c5e 100644 --- a/plugins/example/pom.xml +++ b/plugins/example/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/example/src/main/resources/HelloWorldSpeech.aim.xml b/plugins/example/src/main/resources/HelloWorldSpeech.aim.xml index cfc1d1e76..bcfd51a38 100644 --- a/plugins/example/src/main/resources/HelloWorldSpeech.aim.xml +++ b/plugins/example/src/main/resources/HelloWorldSpeech.aim.xml @@ -18,7 +18,7 @@ - how many times should i say hello? + How many times should i say hello? {int} times diff --git a/plugins/mensa/.settings/org.eclipse.jdt.core.prefs b/plugins/mensa/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/plugins/mensa/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/plugins/mensa/.settings/org.eclipse.jdt.ui.prefs b/plugins/mensa/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/plugins/mensa/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/plugins/mensa/.settings/org.eclipse.m2e.core.prefs b/plugins/mensa/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/mensa/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/mensa/pom.xml b/plugins/mensa/pom.xml new file mode 100644 index 000000000..313400ff7 --- /dev/null +++ b/plugins/mensa/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + amy-plugin-mensa + Amy plugin mensa + The Amy mensa plugin + + de.unistuttgart.iaas.amyassist + amy-plugin + 0.6.0 + ../pom.xml + + + + org.apache.httpcomponents + httpclient + 4.5.6 + + + org.apache.httpcomponents + fluent-hc + 4.5.6 + + + + org.ccil.cowan.tagsoup + tagsoup + 1.2 + + + + + diff --git a/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/Dish.java b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/Dish.java new file mode 100644 index 000000000..d6cc0647d --- /dev/null +++ b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/Dish.java @@ -0,0 +1,84 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.mensa; + +import java.util.Locale; + +/** + * Model class representing a dish + * + * @author Benno Krauß + */ +public class Dish { + + enum Category { + VORSPEISE, + HAUPTGERICHT, + BEILAGE, + DESSERT, + BUFFET + } + + private Category category; + private String name; + private float discountedPrice; + private float regularPrice; + + public Dish(Category category, String name, float discountedPrice, float regularPrice) { + this.category = category; + this.name = name; + this.discountedPrice = discountedPrice; + this.regularPrice = regularPrice; + } + + public Category getCategory() { + return category; + } + + public String getName() { + return name; + } + + public float getDiscountedPrice() { + return discountedPrice; + } + + public float getRegularPrice() { + return regularPrice; + } + + @Override + public String toString() { + return "Dish{" + + "category='" + category + '\'' + + ", name='" + name + '\'' + + ", discountedPrice=" + discountedPrice + + ", regularPrice=" + regularPrice + + '}'; + } + + public String humanReadableString() { + return String.format(Locale.ROOT,"%s for €%.02f", this.name, this.discountedPrice); + } +} diff --git a/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaLogic.java b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaLogic.java new file mode 100644 index 000000000..ba67b3500 --- /dev/null +++ b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaLogic.java @@ -0,0 +1,93 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.mensa; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import org.apache.http.client.fluent.Content; +import org.apache.http.client.fluent.Form; +import org.apache.http.client.fluent.Request; +import org.ccil.cowan.tagsoup.jaxp.SAXFactoryImpl; +import org.slf4j.Logger; +import org.xml.sax.SAXException; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; +import java.util.Collections; +import java.util.List; + +/** + * Mensa logic class + * + * @author Benno Krauß + */ +@Service +public class MensaLogic { + + private static final String HOST = "https://sws2.maxmanager.xyz/inc/ajax-php_konnektor.inc.php"; + + @Reference + private Logger logger; + + List getDishes(LocalDate date) { + + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + + LocalDate previousMonday = date.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)); + LocalDate nextMonday = date.plusDays(7); + + try { + Content c = Request.Post(HOST) + .addHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + .bodyForm( + Form.form() + .add("func", "make_spl") + .add("locId", "3") + .add("date", date.format(formatter)) + .add("lang", "de") + .add("startThisWeek", previousMonday.format(formatter)) + .add("startNextWeek", nextMonday.format(formatter)) + .build() + ).execute().returnContent(); + + String xml = c.asString(); + + SAXParser parser = new SAXFactoryImpl().newSAXParser(); + XmlHandler handler = new XmlHandler(); + parser.parse(new ByteArrayInputStream((xml).getBytes(StandardCharsets.UTF_8)), handler); + + return handler.getMeals(); + } catch (IOException | ParserConfigurationException | SAXException e) { + logger.error("Couldn't get meals from mensa: ", e); + return Collections.emptyList(); + } + } +} diff --git a/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaSpeech.java b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaSpeech.java new file mode 100644 index 000000000..7cedd0bf6 --- /dev/null +++ b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/MensaSpeech.java @@ -0,0 +1,76 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.mensa; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; + +import java.time.LocalDate; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * Mensa plugin + * + * @author Benno Krauß + */ +@SpeechCommand +public class MensaSpeech { + + @Reference + private MensaLogic logic; + + @Intent + public String getFood(Map entities) { + + LocalDate date = LocalDate.now(); + String timeString = "the"; + + EntityData data = entities.get("amydate"); + if (data != null) { + date = data.getDate(); + if (data.getString().equals("today") || data.getString().equals("tomorrow")) + timeString = data.getString() + "'s"; + } + + List dishes = logic.getDishes(date); + + if (dishes.isEmpty()) { + return "I don't know about the lunch. Maybe try again later."; + } else { + StringBuilder r = new StringBuilder(); + r.append("Here's "); + r.append(timeString); + r.append(" lunch menu for the cafeteria. These are the main dishes: \n"); + String mainDishes = dishes.stream().filter(d -> d.getCategory() == Dish.Category.HAUPTGERICHT) + .map(Dish::humanReadableString).collect(Collectors.joining(". \n")); + r.append(mainDishes); + + return r.toString(); + } + } +} diff --git a/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/XmlHandler.java b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/XmlHandler.java new file mode 100644 index 000000000..99ea278a0 --- /dev/null +++ b/plugins/mensa/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/mensa/XmlHandler.java @@ -0,0 +1,129 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.mensa; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.DefaultHandler; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Class for parsing the mensa html and retrieving the dishes + * This relies on the proprietary html format, obviously. + * + * It has to be used in conjunction with an SAXParser. + * After the parsing, the result can be accessed by using the getMeals-method + * + * @author Benno Krauß + */ +public class XmlHandler extends DefaultHandler { + + public List getMeals() { + return result; + } + + private List result = new ArrayList<>(); + + private String category = null; + private boolean isCategory = false; + + private boolean isDish = false; + + private String name = null; + private float discountedPrice = Float.NEGATIVE_INFINITY; + + private boolean priceNext = false; + + /** + * Whether an xml element with these attributes is kind of a CSS class className + * @param attributes attributes object from xml parser + * @param className css class name + * @return true if it is member of this css class + */ + private boolean isMemberOfClass(Attributes attributes, String className) { + + String classNames = attributes.getValue("class"); + return Arrays.asList((classNames != null ? classNames : " ").split(" ")).contains(className); + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { + + if (isMemberOfClass(attributes, "gruppenname")) { + isCategory = true; + } + + if (isMemberOfClass(attributes, "splMeal")) { + isDish = true; + } + + } + + @SuppressWarnings("squid:S1244") + @Override + public void characters(char[] ch, int start, int length) throws SAXException { + + String content = new String(ch, start, length).trim(); + + if (isCategory) { + category = content; + isCategory = false; + } + + if (isDish && content.length() > 0) { + name = content; + isDish = false; + } + + if (content.contains("€") && name != null) { + priceNext = true; + } + + if (priceNext) { + try { + // Remove non-breaking spaces and replace decimal separator with dot + String contentForFloatParsing = content.replaceAll("(^\\h*)|(\\h*$)","") + .replaceAll(",", "."); + + float f = Float.parseFloat(contentForFloatParsing); + + if (discountedPrice == Float.NEGATIVE_INFINITY) { + discountedPrice = f; + } else { + // now f is the regular price + result.add(new Dish(Dish.Category.valueOf(category), name, discountedPrice, f)); + discountedPrice = Float.NEGATIVE_INFINITY; + priceNext = false; + name = null; + } + + } catch (NumberFormatException e) { + // do nothing + } + } + } +} diff --git a/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-mensa.natlangMeta b/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-mensa.natlangMeta new file mode 100644 index 000000000..e10d5983c --- /dev/null +++ b/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-mensa.natlangMeta @@ -0,0 +1,2 @@ +.aims: +MensaSpeech.aim.xml \ No newline at end of file diff --git a/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..abfca1b67 --- /dev/null +++ b/plugins/mensa/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.mensa.MensaLogic diff --git a/plugins/mensa/src/main/resources/MensaSpeech.aim.xml b/plugins/mensa/src/main/resources/MensaSpeech.aim.xml new file mode 100644 index 000000000..0ce253197 --- /dev/null +++ b/plugins/mensa/src/main/resources/MensaSpeech.aim.xml @@ -0,0 +1,7 @@ + + + + ([tell me] what [s|is|does] (the mensa offer|for (lunch|dinner))|i wonder if i will eat) [{amydate}] + + \ No newline at end of file diff --git a/plugins/navigation/pom.xml b/plugins/navigation/pom.xml index c768e24c2..f63c98dbc 100644 --- a/plugins/navigation/pom.xml +++ b/plugins/navigation/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/BestTransportResult.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/BestTransportResult.java index b2eecc16c..1e73b6753 100644 --- a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/BestTransportResult.java +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/BestTransportResult.java @@ -23,16 +23,24 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.navigation; -import javax.xml.bind.annotation.XmlRootElement; - import com.google.maps.model.DirectionsLeg; import com.google.maps.model.DirectionsRoute; +import com.google.maps.model.LatLng; import com.google.maps.model.TravelMode; +import de.unistuttgart.iaas.amyassist.amy.plugin.navigation.rest.WidgetRouteInfo; + +import javax.xml.bind.annotation.XmlRootElement; +import java.io.UnsupportedEncodingException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Locale; /** * This class is needed to store the best route and the transport type * - * @author Lars Buttgereit, Muhammed Kaya + * @author Lars Buttgereit, Muhammed Kaya, Benno Krauß */ @XmlRootElement public class BestTransportResult { @@ -41,6 +49,7 @@ public class BestTransportResult { private static final String THE_ROUTE_IS = "The route is "; private static final String LONG_AND_NEED = " long and you will need "; + private static final String TIME_FORMAT = "HH:mm"; /** * constructor @@ -93,28 +102,80 @@ public String routeToLongString() { * @return */ public String routeToShortString() { - if (this.route != null && this.route.legs != null) { + if (this.route != null && this.route.legs != null && this.route.legs[0] != null) { DirectionsLeg leg = this.route.legs[0]; + StringBuilder builder = new StringBuilder(); switch (this.mode) { case DRIVING: if (leg.durationInTraffic != null) { - return THE_ROUTE_IS.concat(leg.distance.humanReadable) - .concat(LONG_AND_NEED.concat(leg.durationInTraffic.humanReadable)) - .concat(" time in traffic"); + return builder.append(THE_ROUTE_IS).append(leg.distance.humanReadable).append(LONG_AND_NEED) + .append(leg.durationInTraffic.humanReadable).append(" time in traffic").toString(); } - return THE_ROUTE_IS.concat(leg.distance.humanReadable) - .concat(LONG_AND_NEED.concat(leg.duration.humanReadable)).concat(" time"); + return builder.append(THE_ROUTE_IS).append(leg.distance.humanReadable).append(LONG_AND_NEED) + .append(leg.duration.humanReadable).append(" time").toString(); case TRANSIT: - return "Departure time is ".concat(leg.departureTime.toString("HH:mm")).concat(", arrival time is ") - .concat(leg.arrivalTime.toString("HH:mm")); + int i = 0; + while (leg.steps[i].transitDetails == null) { + i++; + } + if (i > 0) { + return builder.append("You should go at ").append(leg.departureTime.toString(TIME_FORMAT)) + .append(". departure time of the first public transport is ") + .append(leg.steps[i].transitDetails.departureTime.toString(TIME_FORMAT)) + .append(" at the station ").append(leg.steps[i].transitDetails.departureStop.name) + .append(" with line ").append(leg.steps[i].transitDetails.line.shortName).append(" to ") + .append(leg.steps[i].transitDetails.headsign).append(". The arrival time is ") + .append(leg.arrivalTime.toString(TIME_FORMAT)).toString(); + } + return builder.append("Departure is ").append(leg.departureTime.toString(TIME_FORMAT)).append(" at ") + .append(leg.steps[i].transitDetails.departureStop.name).append(" with line ") + .append(leg.steps[i].transitDetails.line.shortName).append(" to ") + .append(leg.steps[i].transitDetails.headsign).append(", arrival time is ") + .append(leg.arrivalTime.toString(TIME_FORMAT)).toString(); case BICYCLING: - return THE_ROUTE_IS.concat(leg.distance.humanReadable) - .concat(LONG_AND_NEED.concat(leg.duration.humanReadable)).concat(" time"); + return builder.append(THE_ROUTE_IS).append(leg.distance.humanReadable).append(LONG_AND_NEED) + .append(leg.duration.humanReadable).append(" time").toString(); default: break; } } - return ""; + return "No route found"; + } + + private String urlEncode(String string) throws UnsupportedEncodingException { + return URLEncoder.encode(string, StandardCharsets.UTF_8.name()); + } + + public WidgetRouteInfo routeToWidgetInfo(String mapsStaticAPIKey) { + + try { + if (this.route.legs != null && this.route.legs.length > 0) { + LatLng start = this.route.legs[0].startLocation; + LatLng end = this.route.legs[0].endLocation; + String travelMode = this.mode.toUrlValue(); + + // Locale.ROOT is used to force points as decimal separators as those are requires by the gmaps web api + String linkURLString = String.format(Locale.ROOT, "https://www.google.com/maps/dir/?api=1&" + + "origin=%f,%f&destination=%f,%f&travelmode=%s", + start.lat, start.lng, end.lat, end.lng, travelMode); + URL link = new URL(linkURLString); + + + String imageURLString = String.format(Locale.ROOT, "https://maps.googleapis.com/maps/api/staticmap?" + + "size=300x300&path=enc:%s&markers=%s&markers=%s&key=%s", + this.urlEncode(this.route.overviewPolyline.getEncodedPath()), + this.urlEncode("color:blue|label:S|" + start.toUrlValue()), + this.urlEncode("color:red|label:E|" + end.toUrlValue()), + mapsStaticAPIKey + ); + URL image = new URL(imageURLString); + + return new WidgetRouteInfo(image, link, "Start in Google Maps"); + } + } catch (UnsupportedEncodingException | MalformedURLException e) { + throw new IllegalStateException("Couldn't create widget info", e); + } + return null; } /** diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionApiLogic.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionApiLogic.java index dad5719d1..e9f483d07 100644 --- a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionApiLogic.java +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionApiLogic.java @@ -228,7 +228,7 @@ private DirectionsRoute findBestArrivalTime(DirectionsRoute[] routes) { * transfer a string to the apt TravelMode of the api * * @param mode - * supported Strings: driving, car, bicycling, transit, public transport, transport, walking, walk + * supported Strings: driving, car, bicycling, transit, public transport, transport, walking, walk, etc. * @return apt travelMode */ public TravelMode getTravelMode(String mode) { @@ -242,6 +242,7 @@ public TravelMode getTravelMode(String mode) { return TravelMode.BICYCLING; case "transit": case "public transport": + case "public transit": case "transport": return TravelMode.TRANSIT; case "walking": @@ -252,4 +253,8 @@ public TravelMode getTravelMode(String mode) { } } + String getStaticAPIKey() { + return this.calls.getStaticAPIKey(); + } + } diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionsApiCalls.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionsApiCalls.java index f44a35240..d3cadf884 100644 --- a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionsApiCalls.java +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/DirectionsApiCalls.java @@ -57,6 +57,7 @@ public class DirectionsApiCalls { private GeoApiContext context; private static final String ERROR_TAG = "Google API Error"; + private static final String GOOGLE_STATIC_API_KEY = "GOOGLE_STATIC_API_KEY"; /** * load the api key and create the context to create calls @@ -136,4 +137,13 @@ private DirectionsRoute[] errorHandling(DirectionsApiRequest request) { } return new DirectionsRoute[0]; } + + /** + * Get the "google maps static" api-key from the configuration. + * This key is restricted to websites from our domain (via referer-header) + * @return api key + */ + String getStaticAPIKey() { + return this.properties.getProperty(GOOGLE_STATIC_API_KEY); + } } diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/NavigationSpeech.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/NavigationSpeech.java index 7b34363a4..f0cb0f90e 100644 --- a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/NavigationSpeech.java +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/NavigationSpeech.java @@ -24,11 +24,13 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.navigation; import java.time.LocalDateTime; -import java.time.LocalTime; +import java.time.ZoneId; +import java.time.format.DateTimeParseException; import java.util.Arrays; import java.util.List; import java.util.Map; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.*; import org.joda.time.DateTime; import org.joda.time.DateTimeFieldType; import org.joda.time.ReadableInstant; @@ -37,10 +39,6 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProvider; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; /** * Speech class for the navigation plugin @@ -62,7 +60,7 @@ public class NavigationSpeech { @Reference private Logger logger; - private static final String WRONG_PLACE = "One or more places are not in the registry"; + private static final String WRONG_TIME = "Time Input is wrong. Please try again"; private static final String END_KEY = "end"; private static final String START_KEY = "start"; @@ -75,23 +73,24 @@ public class NavigationSpeech { */ @Intent() public String goToAt(Map entities) { - LocalDateTime now = this.environment.getCurrentLocalDateTime(); - LocalTime inputTime = entities.get("time").getTime(); - if (inputTime != null) { - DateTime time = new DateTime(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), inputTime.getHour(), - inputTime.getMinute()); + if (entities.get("time") != null) { + DateTime time = dateTimeConversion(entities.get("time")); ReadableInstant outputTime = null; outputTime = this.logic.whenIHaveToGo( - cutEndWords(this.registryConnection.getAddress(entities.get(START_KEY).getString()), "to"), - cutEndWords(this.registryConnection.getAddress(entities.get(END_KEY).getString()), "by"), + this.registryConnection.getAddress(entities.get(START_KEY).getString()), + this.registryConnection.getAddress(entities.get(END_KEY).getString()), this.logic.getTravelMode(entities.get("mode").getString().trim()), time); if (outputTime != null) { + if (outputTime.get(DateTimeFieldType.minuteOfHour()) > 9) { + return "You should go at ".concat(String.valueOf(outputTime.get(DateTimeFieldType.hourOfDay()))) + .concat(":").concat(String.valueOf(outputTime.get(DateTimeFieldType.minuteOfHour()))); + } return "You should go at ".concat(String.valueOf(outputTime.get(DateTimeFieldType.hourOfDay()))) - .concat(":").concat(String.valueOf(outputTime.get(DateTimeFieldType.minuteOfHour()))); + .concat(":0").concat(String.valueOf(outputTime.get(DateTimeFieldType.minuteOfHour()))); } return "You are too late"; } - return WRONG_PLACE; + return WRONG_TIME; } /** @@ -103,20 +102,17 @@ public String goToAt(Map entities) { */ @Intent() public String bestTransport(Map entities) { - LocalDateTime now = this.environment.getCurrentLocalDateTime(); - LocalTime inputTime = entities.get("time").getTime(); - if (inputTime != null) { - DateTime time = new DateTime(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), inputTime.getHour(), - inputTime.getMinute()); + if (entities.get("time") != null) { + DateTime time = dateTimeConversion(entities.get("time")); BestTransportResult result = this.logic.getBestTransportInTime( - cutEndWords(this.registryConnection.getAddress(entities.get(START_KEY).getString()), "to"), - cutEndWords(this.registryConnection.getAddress(entities.get(END_KEY).getString()), "at"), time); + this.registryConnection.getAddress(entities.get(START_KEY).getString()), + this.registryConnection.getAddress(entities.get(END_KEY).getString()), time); if (result != null) { return "The best transport Mode is ".concat(result.getMode().toString()).concat(".\n") .concat(result.routeToShortString()); } } - return WRONG_PLACE; + return WRONG_TIME; } /** @@ -127,29 +123,39 @@ public String bestTransport(Map entities) { * @return a output string */ @Intent() - public String routeFromtTo(Map entities) { - return this.logic - .fromToWithDeparture( - cutEndWords(this.registryConnection.getAddress(entities.get(START_KEY).getString()), "to"), - cutEndWords(this.registryConnection.getAddress(entities.get(END_KEY).getString()), "by"), - this.logic.getTravelMode(entities.get("mode").getString().trim()), DateTime.now()) - .routeToShortString(); + public Response routeFromtTo(Map entities) { + + DateTime time = entities.get("time") != null ? dateTimeConversion(entities.get("time")) : DateTime.now(); + + BestTransportResult result = this.logic.fromToWithDeparture( + this.registryConnection.getAddress(entities.get(START_KEY).getString()), + this.registryConnection.getAddress(entities.get(END_KEY).getString()), + this.logic.getTravelMode(entities.get("mode").getString().trim()), + time + ); + + return Response.text(result.routeToShortString()).widget("app-route-widget") + .attachment(result.routeToWidgetInfo(this.logic.getStaticAPIKey())).build(); } /** - * helper method to cut words at the end for example to or by that comes from the speech + * Transform the LocalDateTime or LocalTime from EntityData to a DateTime from the api. * - * @param location - * location input - * @param cut - * string to cut - * @return the location string without the end word + * @param inputTime + * the EntityData from the time entity + * @return a DateTime object */ - private String cutEndWords(String location, String cut) { - if (location.endsWith(cut)) { - return location.substring(0, location.length() - cut.length() - 1); + private DateTime dateTimeConversion(EntityData inputTime) { + DateTime outputTime = null; + try { + outputTime = new DateTime( + inputTime.getDateTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + } catch (DateTimeParseException e) { + LocalDateTime now = this.environment.getCurrentLocalDateTime(); + outputTime = new DateTime(now.getYear(), now.getMonthValue(), now.getDayOfMonth(), + inputTime.getTime().getHour(), inputTime.getTime().getMinute()); } - return location; + return outputTime; } /** diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/RegistryConnection.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/RegistryConnection.java index 8facca2f1..3f38479cc 100644 --- a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/RegistryConnection.java +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/RegistryConnection.java @@ -59,9 +59,9 @@ public String getAddress(String name) { if (!locations.isEmpty()) { address = locations.get(0); } else { - // Try to find by name + // Try to find by name or tag ignoring case for (Location location : this.locationRegistry.getAll()) { - if (location.getName().equals(name)) { + if (name.equalsIgnoreCase(location.getName()) || name.equalsIgnoreCase(location.getTag())) { address = location; break; } diff --git a/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/rest/WidgetRouteInfo.java b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/rest/WidgetRouteInfo.java new file mode 100644 index 000000000..37d58ef88 --- /dev/null +++ b/plugins/navigation/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/navigation/rest/WidgetRouteInfo.java @@ -0,0 +1,56 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.navigation.rest; + +import java.net.URL; + +/** + * + * A class for an object that supplies info to the maps widget in the webapp + * + * @author Benno Krauß + */ +public class WidgetRouteInfo { + private URL imageURL; + private URL link; + private String linkText; + + public WidgetRouteInfo(URL imageURL, URL link, String linkText) { + this.imageURL = imageURL; + this.link = link; + this.linkText = linkText; + } + + public URL getImageURL() { + return imageURL; + } + + public URL getLink() { + return link; + } + + public String getLinkText() { + return linkText; + } +} diff --git a/plugins/navigation/src/main/resources/NavigationSpeech.aim.xml b/plugins/navigation/src/main/resources/NavigationSpeech.aim.xml index fe012db49..24832b93e 100644 --- a/plugins/navigation/src/main/resources/NavigationSpeech.aim.xml +++ b/plugins/navigation/src/main/resources/NavigationSpeech.aim.xml @@ -2,11 +2,8 @@ - when [do] i have to go [by {mode}] + when [do] i have to (go|leave) [by {mode}] - - {amytime} - start @@ -26,52 +23,53 @@ ({endregistry}|{endwildcard}) - (car|transit|bike|bicycle) + (car|[public] (transport|transit)|bike|bicycle) + + + ({amytime}|{amydatetime}) - - by car, transit or bike? - [by] {mode} - - from which location? + From which location? [from] {start} - - to which location? + To which location? [to] {end} + + By car, public transport or bike? + [by] {mode} + - when do you want to arrive? + When do you want to arrive? [at] {time} - when [do] i have to go from {start} {end} [{mode}] [at {time}] - + when [do] i have to go from {start} to {end} [{mode}] [at {time}] - - {amytime} - - + to + + - + by + + - (car|transit|bike|bicycle) + (car|[public] (transport|transit)|bike|bicycle) + + + ({amytime}|{amydatetime}) - by car, transit or bike? - [by] {mode} + By car, public transport or bike? + [by] {mode} - when do you want to arrive? + When do you want to arrive? [at] {time} @@ -80,9 +78,6 @@ ref="de.unistuttgart.iaas.amyassist.amy.plugin.navigation.NavigationSpeech.bestTransport"> best transport - - ({amytime}) - start @@ -101,14 +96,16 @@ ({endregistry}|{endwildcard}) + + ({amytime}|{amydatetime}) + - from which location? + From which location? [from] {start} - - to which location? + To which location? [to] {end} @@ -119,28 +116,27 @@ - best transport from {start} {end} [{time}] + best transport from {start} to {end} [at {time}] - ({amytime}) + ({amytime}|{amydatetime}) - + to + + - + at + + + When are you leaving? - {time} + [at] {time} - - - get route [by {mode}] + route [by {mode}] start @@ -161,36 +157,45 @@ ({endregistry}|{endwildcard}) - (car|transit|bike|bicycle) + (car|[public] (transport|transit)|bike|bicycle) + + + ({amytime}|{amydatetime}) - from which location? + From which location? [from] {start} - - to which location? + To which location? [to] {end} - by car, transit or bike? - [by] {mode} + By car, public transport or bike? + [by] {mode} + + + When are you leaving? + {time} - get route from {start} {end} {mode} + [route] from {start} to {end} by {mode} [[at]{time}] - + to + + - + by + + - (car|transit|bike) + (car|[public] (transport|transit)|bike|bicycle) + + + ({amytime}|{amydatetime}) diff --git a/plugins/pom.xml b/plugins/pom.xml index 06c14fbb7..6698c0b7f 100644 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -9,7 +9,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml @@ -83,7 +83,7 @@ ${project.artifactId} ${project.version} - ${project.artifactId} + ${project.name} ${project.version} ${project.groupId} ${project.groupId}.${project.artifactId} diff --git a/plugins/social/.settings/org.eclipse.jdt.core.prefs b/plugins/social/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/plugins/social/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/plugins/social/.settings/org.eclipse.jdt.ui.prefs b/plugins/social/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/plugins/social/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/plugins/social/.settings/org.eclipse.m2e.core.prefs b/plugins/social/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/social/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/social/pom.xml b/plugins/social/pom.xml new file mode 100644 index 000000000..7efbc5be5 --- /dev/null +++ b/plugins/social/pom.xml @@ -0,0 +1,22 @@ + + 4.0.0 + amy-plugin-social + Amy plugin social + The Amy social plugin + + de.unistuttgart.iaas.amyassist + amy-plugin + 0.6.0 + ../pom.xml + + + + + com.eclipsesource.minimal-json + minimal-json + 0.9.5 + + + diff --git a/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/JokeAPICaller.java b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/JokeAPICaller.java new file mode 100644 index 000000000..924143db8 --- /dev/null +++ b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/JokeAPICaller.java @@ -0,0 +1,94 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.social; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.UriBuilder; + +import org.slf4j.Logger; + +import com.eclipsesource.json.Json; +import com.eclipsesource.json.JsonValue; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; + +/** + * This class is responsible for calling the RESTful Joke APIs + * + * @author Patrick Singer + */ +@Service +public class JokeAPICaller { + + private static final String DAD_JOKES_API = "https://icanhazdadjoke.com"; + + private List jokeAPIs; + + @Reference + private Logger logger; + + /** + * Get a joke from one of the given APIs (random) + * + * @return a random joke + */ + protected String getRandomJoke() { + Random r = new Random(); + final int apiIndex = r.nextInt(this.jokeAPIs.size()); + return callJokeAPI(this.jokeAPIs.get(apiIndex)); + } + + private String callJokeAPI(String jokeURL) { + WebTarget target = ClientBuilder.newClient().target(UriBuilder.fromPath(jokeURL)); + try (Response response = target.request(MediaType.APPLICATION_JSON).get()) { + if (response.getStatus() != 200) { + return "Calling API failed"; + } + + final String responseString = response.readEntity(String.class); + + if (responseString.startsWith("{")) { + JsonValue json = Json.parse(responseString); + return json.asObject().get("joke").toString().replaceAll("\"", ""); + } + return responseString; + } + + } + + @PostConstruct + private void init() { + this.jokeAPIs = new ArrayList<>(); + this.jokeAPIs.add(DAD_JOKES_API); + } +} diff --git a/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialConstants.java b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialConstants.java new file mode 100644 index 000000000..bad002f78 --- /dev/null +++ b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialConstants.java @@ -0,0 +1,81 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.social; + +/** + * Constant answers + * @author Felix Burk, Florian Bauer + */ +class SocialConstants { + + + private SocialConstants() { + //hide constructor + } + + /** + * greeting constant + */ + static final String[] greeting = { + "Hi", "Hello there", "Good to see you" + }; + + /** + * whatsup constant + */ + static final String[] whatsUp = { + "Not much", "Taking over the world", "Answering your questions", "Doing my duty.", + "They're taking the Hobbits to Isengard!" + }; + + /** + * how are you constant + */ + static final String[] howAreYou = { + "Fine, thanks", "I'm feeling pretty great!" + }; + + /** + * how are you constant + */ + static final String[] myNameIs = { + "They call me Amy.", "My name is Amy, nice to meet you.", "Amy is my name. How can I assist you?" + }; + + /** + * fox says constant + */ + static final String[] foxSays = { + "Ring-ding-ding-ding-dingeringeding!", "Wa-pa-pa-pa-pa-pa-pow!", "Hatee-hatee-hatee-ho!", + "Joff-tchoff-tchoffo-tchoffo-tchoff!" + }; + + /** + * one does not simply constant + */ + static final String[] oneDoesNotSimply = { + "...walk into Mordor.", "...pass Theo on the first try.", "...get the top grade on the project.", + "...build a personal assistant system from scratch." + }; +} diff --git a/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialLogic.java b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialLogic.java new file mode 100644 index 000000000..4d4d21d7c --- /dev/null +++ b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialLogic.java @@ -0,0 +1,204 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.social; + +import java.util.HashMap; +import java.util.Map; +import java.util.Random; + +import de.unistuttgart.iaas.amyassist.amy.core.Configuration; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.NatlangInformation; + +/** + * Logic for this social plugin + * + * @author Felix Burk, Patrick Singer, Florian Bauer + */ +@Service +public class SocialLogic { + + /** + * receive information about possible grammars to use + */ + @Reference + NatlangInformation info; + + /** + * provides information about installed plugins + */ + @Reference + Configuration constants; + + @Reference + private JokeAPICaller joker; + + /** + * map of internal and human readable plugin names the key are human readable names and values are the internal ones + */ + Map pluginNames; + + /** + * initializes plugin information + */ + @PostConstruct + public void init() { + String[] pluginInfo = this.constants.getInstalledPlugins(); + String pluginHumanReadableName; + this.pluginNames = new HashMap<>(); + + for (int i = 0; i < pluginInfo.length; i++) { + pluginHumanReadableName = pluginInfo[i].substring(pluginInfo[i].lastIndexOf("plugin-") + 7, + pluginInfo[i].length()); + this.pluginNames.put(pluginHumanReadableName, pluginInfo[i]); + } + } + + /** + * receive a random name answer + * + * @return her name + */ + String getMyNameIs() { + return generateRandomAnswer(SocialConstants.myNameIs); + } + + + /** + * receive a random greeting + * + * @return the greeting string + */ + String getGreeting() { + return generateRandomAnswer(SocialConstants.greeting); + } + + /** + * receive a random string answer + * + * @return answer + */ + String getWhatsUp() { + return generateRandomAnswer(SocialConstants.whatsUp); + } + + /** + * receive a random string answer + * + * @return answer + */ + String getHowAreYou() { + return generateRandomAnswer(SocialConstants.howAreYou); + } + + /** + * receive a random string answer + * + * @return answer + */ + String getFoxSays() { + return generateRandomAnswer(SocialConstants.foxSays); + } + + /** + * receive a random string answer + * + * @return answer + */ + String getOneDoesNotSimply() { + return generateRandomAnswer(SocialConstants.oneDoesNotSimply); + } + + /** + * receive a number of sample sentences + * + * @param nmbSentences + * number of sentences + * @return the sentences + */ + String[] getSampleSentences(int nmbSentences) { + return this.info.getAnySampleSentences(nmbSentences).toArray(new String[nmbSentences]); + } + + /** + * receive a number of sample sentences containing a keyword + * + * @param nmbSentences + * number of sentences + * @param keyword + * to match + * @return the sentences + */ + String[] getSampleSentencesWithKeyword(String keyword, int nmbSentences) { + return this.info.getSampleSentencesFromKeyword(keyword, nmbSentences).toArray(new String[nmbSentences]); + } + + /** + * provides a list of installed plugin names + * + * @return the list + */ + String[] getInstalledPluginNames() { + return this.pluginNames.keySet().toArray(new String[this.pluginNames.size()]); + + } + + /** + * provides information about a specific plugin + * + * @param pluginname + * name of the plugin + * @return the information + */ + String getPluginInformation(String pluginname) { + if (this.pluginNames.get(pluginname) != null) { + return this.constants.getPluginDescription(this.pluginNames.get(pluginname)); + } + return null; + } + + /** + * Get a random joke from an API + * + * @return a random joke + */ + String getJoke() { + return this.joker.getRandomJoke(); + } + + /** + * generates a random answer from a string array + * + * @param strings + * possible to select + * @return selected string + */ + private String generateRandomAnswer(String[] strings) { + Random rand = new Random(); + int rndm = rand.nextInt(strings.length); + return strings[rndm]; + } +} diff --git a/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialSpeech.java b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialSpeech.java new file mode 100644 index 000000000..c4ac0d7f6 --- /dev/null +++ b/plugins/social/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/social/SocialSpeech.java @@ -0,0 +1,241 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.social; + +import java.util.Map; + +import org.apache.commons.lang3.StringUtils; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; + +/** + * A plugin which handles social interactions + * + * @author Felix Burk, Patrick Singer, Florian Bauer + */ +@Service +@SpeechCommand +public class SocialSpeech { + + @Reference + private SocialLogic logic; + + private static final int NMB_SAMPLE_SENTENCES = 4; + + /** + * returns a greeting + * + * @param entities + * of input + * @return the greeting + */ + @Intent + public String greeting(Map entities) { + return this.logic.getGreeting(); + } + + /** + * greets user back + * + * @param entities + * of input + * @return the greeting + */ + @Intent + public String userName(Map entities) { + return "Nice to meet you, " + entities.get("name").getString(); + } + + /** + * tells user amys name + * + * @param entities + * of input + * @return the greeting + */ + @Intent + public String askForName(Map entities) { + return this.logic.getMyNameIs(); + } + + /** + * get a "whats up" answer + * + * @param entities + * of input + * @return the answer + */ + @Intent + public String whatsUp(Map entities) { + return this.logic.getWhatsUp(); + } + + /** + * get an answer to "how are you" or similar phrases + * + * @param entities + * entities of input + * @return the answer + */ + @Intent + public String howAreYou(Map entities) { + return this.logic.getHowAreYou(); + } + + /** + * returns 4 sample sentences that may be told to amy + * + * @param entities + * of input + * @return the answer + */ + @Intent + public String sampleSentences(Map entities) { + StringBuilder s = new StringBuilder(); + s.append("You may ask me for example"); + + if (entities.get("pluginname") == null) { + s.append("\n" + StringUtils.join(this.logic.getSampleSentences(NMB_SAMPLE_SENTENCES), "\n")); + } else { + String keyword = entities.get("pluginname").getString(); + String[] results = this.logic.getSampleSentencesWithKeyword(keyword, NMB_SAMPLE_SENTENCES); + if (results[0] != null && !results[0].equals("")) { + s.append("\n" + StringUtils + .join(this.logic.getSampleSentencesWithKeyword(keyword, NMB_SAMPLE_SENTENCES), "\n")); + } else { + return "I don't know anything about this"; + } + } + return s.toString(); + } + + /** + * returns the names of all installed plugins + * + * @param entities + * of input + * @return the answer + */ + @Intent + public String getInstalledPlugins(Map entities) { + StringBuilder s = new StringBuilder(); + s.append("These are my currently installed plugins"); + s.append("\n" + StringUtils.join(this.logic.getInstalledPluginNames(), ", ")); + s.append("\nfeel free to add more!"); + return s.toString(); + } + + /** + * returns the description of a plugin + * + * @param entities + * of input + * @return the answer + */ + @Intent + public String tellMeAboutPlugin(Map entities) { + String pluginName = entities.get("plugin").getString(); + String info = this.logic.getPluginInformation(pluginName); + if (info != null) { + return info; + } + return "i don't know about " + pluginName + " yet"; + } + + /** + * Get a random joke + * + * @param entities + * entities of input + * @return a joke + */ + @Intent + public String tellJoke(Map entities) { + return this.logic.getJoke(); + } + + /** + * Get a fox answer + * + * @param entities + * entities of input + * @return a fox answer + */ + @Intent + public String foxSays(Map entities) { + return this.logic.getFoxSays(); + } + + /** + * Answers to Valar Morghulis + * + * @param entities + * entities of input + * @return Valar Dohaeris. + */ + @Intent + public String valar(Map entities) { + return "Valar Dohaeris."; + } + + /** + * Repeats what you have said + * + * @param entities + * entities of input + * @return what you have said after repeat/say + */ + @Intent + public String parrot(Map entities) { + return entities.get("phrase").getString(); + } + + /** + * They're taking the Hobbits to Isengard! + * + * @param entities + * entities of input + * @return They're taking the Hobbits to Isengard! + */ + @Intent + public String legolas(Map entities) { + return "They're taking the Hobbits to Isengard!"; + } + + /** + * Finishes "one does not simply" + * + * @param entities + * entities of input + * @return the answer + */ + @Intent + public String oneDoesNotSimply(Map entities) { + return this.logic.getOneDoesNotSimply(); + } +} diff --git a/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-social.natlangMeta b/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-social.natlangMeta new file mode 100644 index 000000000..b6321a85a --- /dev/null +++ b/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-social.natlangMeta @@ -0,0 +1,2 @@ +.aims: +SocialSpeech.aim.xml \ No newline at end of file diff --git a/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..6631da694 --- /dev/null +++ b/plugins/social/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1,2 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.social.SocialLogic +de.unistuttgart.iaas.amyassist.amy.plugin.social.JokeAPICaller diff --git a/plugins/social/src/main/resources/SocialSpeech.aim.xml b/plugins/social/src/main/resources/SocialSpeech.aim.xml new file mode 100644 index 000000000..8b2f5092a --- /dev/null +++ b/plugins/social/src/main/resources/SocialSpeech.aim.xml @@ -0,0 +1,79 @@ + + + + (hello|hi|good (morning|evening|day)) [amy] + + + (i am | my name is) {name} + + + * + + + + + (what|whats) (is|s) your name + + + ((what|whats) (is|s) (up|going on)|whatsapp) + + + how (are you [feeling]| do you [feel]) + + + (what (can|may) i ask you [about] | help [me]) [[with] [the plugin] {pluginname}] + + + * + + + + + (what plugins are installed | (tell me about your [installed] | installed) plugins) + + + tell me about {plugin} + + + * + + + + + ((tell me|say) (a joke|something funny)|make me laugh) + + + what [does] the fox say + + + valar morghulis + + + what do your (elfeyes|elf eyes) see + + + 1 does not simply + + + (say|repeat) {phrase} + + + * + + + + diff --git a/plugins/spotify/pom.xml b/plugins/spotify/pom.xml index 6640c43ad..29ce22d9f 100644 --- a/plugins/spotify/pom.xml +++ b/plugins/spotify/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICalls.java b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICalls.java index 8e071d2a0..0afbcfc3d 100644 --- a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICalls.java +++ b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICalls.java @@ -356,7 +356,7 @@ public boolean playListFromUri(String uri) { * @return a boolean. true if the command was executed, else if the command failed */ public boolean resume() { - if (checkPlayerState()) { + if (checkPlayerState() && !getIsPlaying()) { StartResumeUsersPlaybackRequest startResumeUsersPlaybackRequest = getSpotifyApi().startResumeUsersPlayback() .device_id(this.userData.getCurrentDeviceId()).build(); return exceptionHandlingWithBoolean(startResumeUsersPlaybackRequest); @@ -370,7 +370,7 @@ public boolean resume() { * @return a boolean. true if the command was executed, else if the command failed */ public boolean pause() { - if (checkPlayerState()) { + if (checkPlayerState() && getIsPlaying()) { PauseUsersPlaybackRequest pauseUsersPlaybackRequest = getSpotifyApi().pauseUsersPlayback() .device_id(this.userData.getCurrentDeviceId()).build(); return exceptionHandlingWithBoolean(pauseUsersPlaybackRequest); @@ -454,7 +454,11 @@ public CurrentlyPlayingContext getCurrentPlayingContext() { * @return true if currently playing, false otherwise */ public boolean getIsPlaying() { - return getCurrentPlayingContext().getIs_playing(); + CurrentlyPlayingContext context = getCurrentPlayingContext(); + if (context != null) { + return getCurrentPlayingContext().getIs_playing(); + } + return false; } /** diff --git a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeech.java b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeech.java index 816cf6b98..592453e18 100644 --- a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeech.java +++ b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeech.java @@ -29,10 +29,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProvider; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.*; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.*; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic.DeviceLogic; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic.PlayerLogic; @@ -90,9 +87,16 @@ public String getDevices(Map entites) { * @return the speech output string */ @Intent() - public String getCurrentSong(Map entites) { + public Response getCurrentSong(Map entites) { TrackEntity track = this.playerLogic.getCurrentSong(); - return (track != null) ? "track: " + track.toString() : "No song is playing"; + + if (track != null) { + return Response.text("track: " + track.toString()) + .widget("app-current-song") + .build(); + } + + return Response.text("No song is playing").build(); } /** @@ -105,16 +109,20 @@ public String getCurrentSong(Map entites) { @Intent() public String getPlaylists(Map entites) { StringBuilder builder = new StringBuilder(); + int amount = LIMIT_FOR_SEARCH; + if (entites.get("number") != null) { + amount = entites.get("number").getNumber(); + } if (entites.get("type").getString() != null) { switch (entites.get("type").getString()) { case "featured": - for (PlaylistEntity playlist : this.search.searchFeaturedPlaylists(LIMIT_FOR_SEARCH)) { + for (PlaylistEntity playlist : this.search.searchFeaturedPlaylists(amount)) { builder = builder.append(playlist.toString()).append("\n"); } break; case "own": case "on": - for (PlaylistEntity playlist : this.search.searchOwnPlaylists(LIMIT_FOR_SEARCH)) { + for (PlaylistEntity playlist : this.search.searchOwnPlaylists(amount)) { builder = builder.append(playlist.toString()).append("\n"); } break; @@ -155,29 +163,24 @@ public String playSomething(Map entites) { public String control(Map entites) { switch (entites.get("type").getString()) { case "back": - if (this.playerLogic.back()) { - return "back"; - } - return ERROR_MESSAGE; + this.playerLogic.back(); + break; case "skip": - if (this.playerLogic.skip()) { - return "skip"; - } - return ERROR_MESSAGE; + this.playerLogic.skip(); + break; case "pause": case "pass": - if (this.playerLogic.pause()) { - return "pause"; - } - return ERROR_MESSAGE; + case "stop": + this.playerLogic.pause(); + break; case "resume": - if (this.playerLogic.resume()) { - return "resuming"; - } - return ERROR_MESSAGE; + this.playerLogic.resume(); + break; default: return ERROR_MESSAGE; } + return "OK"; + } /** @@ -269,39 +272,40 @@ public String playPlaylistId(Map entites) { * @return the track, album, playist that is now playing */ @Intent() - public String searchASong(Map entites) { + public Response searchASong(Map entites) { switch (entites.get("mode").getString()) { case "track": case "song": List tracks = this.search.searchforTracks(entites.get("name").getString(), 1); if (!tracks.isEmpty() && tracks.get(0) != null) { - return this.playerLogic.playTrack(0).toString(); + return Response.text(this.playerLogic.playTrack(0).toString()) + .widget("app-current-song") + .build(); } break; case "playlist": List playlists = this.search.searchforPlaylists(entites.get("name").getString(), 1); if (!playlists.isEmpty() && playlists.get(0) != null) { - return this.playerLogic.playPlaylist(0, SearchTypes.SEARCH_PLAYLISTS).toString(); + return Response.text(this.playerLogic.playPlaylist(0, SearchTypes.SEARCH_PLAYLISTS).toString()).build(); } break; case "album": List albums = this.search.searchforAlbums(entites.get("name").getString(), 1); if (!albums.isEmpty() && albums.get(0) != null) { - return this.playerLogic.playAlbum(0).toString(); + return Response.text(this.playerLogic.playAlbum(0).toString()).build(); } break; case "artist": - case "artists": List artists = this.search.searchforArtists(entites.get("name").getString(), 1); if (!artists.isEmpty() && artists.get(0) != null) { - return this.playerLogic.playArtist(0).toString(); + return Response.text(this.playerLogic.playArtist(0).toString()).build(); } break; default: break; } - return ERROR_MESSAGE_ELEMENT; + return Response.text(ERROR_MESSAGE_ELEMENT).build(); } /** diff --git a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogic.java b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogic.java index 59e6871ba..272d7fe2f 100644 --- a/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogic.java +++ b/plugins/spotify/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogic.java @@ -61,8 +61,20 @@ public class PlayerLogic { private boolean suppressed = false; private PostSuppressionAction postSuppressionAction = PostSuppressionAction.NONE; + /** + * save a album or playlist uri to play after the speech output + */ + private String collectionUriToPlay; + /** + * save a track uri to play after the speech output + */ + private String trackUriToPlay; + + /** + * actions to be performed after the mute + */ private enum PostSuppressionAction { - NONE, PAUSE + NONE, PAUSE, PLAY_COLLECTION, PLAY_TRACK, SKIP, BACK } private static final int VOLUME_MUTE_VALUE = 0; @@ -113,9 +125,15 @@ public void inputAuthCode(String authCode) { public PlaylistEntity play() { List playLists; playLists = this.search.searchFeaturedPlaylists(5); - if (!playLists.isEmpty() && 1 < playLists.size() - && this.spotifyAPICalls.playListFromUri(playLists.get(1).getUri())) - return playLists.get(1); + if (!playLists.isEmpty()) { + if (this.suppressed) { + this.collectionUriToPlay = playLists.get(0).getUri(); + this.postSuppressionAction = PostSuppressionAction.PLAY_COLLECTION; + } else { + this.spotifyAPICalls.playListFromUri(playLists.get(0).getUri()); + } + return playLists.get(0); + } this.logger.warn("no featured playlist found"); return null; } @@ -130,18 +148,23 @@ public PlaylistEntity play() { * @return a PlaylistEntity */ public PlaylistEntity playPlaylist(int playlistNumber, SearchTypes type) { + PlaylistEntity selectedPlaylist = null; if (type.equals(SearchTypes.FEATURED_PLAYLISTS) && this.search.getFeaturedPlaylists().size() > playlistNumber) { - this.spotifyAPICalls.playListFromUri(this.search.getFeaturedPlaylists().get(playlistNumber).getUri()); - return this.search.getFeaturedPlaylists().get(playlistNumber); - } - if (type.equals(SearchTypes.USER_PLAYLISTS) && this.search.getOwnPlaylists().size() > playlistNumber) { - this.spotifyAPICalls.playListFromUri(this.search.getOwnPlaylists().get(playlistNumber).getUri()); - return this.search.getOwnPlaylists().get(playlistNumber); - } - if (type.equals(SearchTypes.SEARCH_PLAYLISTS) + selectedPlaylist = this.search.getFeaturedPlaylists().get(playlistNumber); + } else if (type.equals(SearchTypes.USER_PLAYLISTS) && this.search.getOwnPlaylists().size() > playlistNumber) { + selectedPlaylist = this.search.getOwnPlaylists().get(playlistNumber); + } else if (type.equals(SearchTypes.SEARCH_PLAYLISTS) && this.search.getPlaylistSearchResults().size() > playlistNumber) { - this.spotifyAPICalls.playListFromUri(this.search.getPlaylistSearchResults().get(playlistNumber).getUri()); - return this.search.getPlaylistSearchResults().get(playlistNumber); + selectedPlaylist = this.search.getPlaylistSearchResults().get(playlistNumber); + } + if (selectedPlaylist != null) { + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.PLAY_COLLECTION; + this.collectionUriToPlay = selectedPlaylist.getUri(); + } else { + this.spotifyAPICalls.playListFromUri(selectedPlaylist.getUri()); + } + return selectedPlaylist; } this.logger.warn(ITME_NOT_FOUND); return null; @@ -156,7 +179,12 @@ public PlaylistEntity playPlaylist(int playlistNumber, SearchTypes type) { */ public TrackEntity playTrack(int trackNumber) { if (this.search.getTrackSearchResults().size() > trackNumber) { - this.spotifyAPICalls.playSongFromUri(this.search.getTrackSearchResults().get(trackNumber).getUri()); + if (this.suppressed) { + this.trackUriToPlay = this.search.getTrackSearchResults().get(trackNumber).getUri(); + this.postSuppressionAction = PostSuppressionAction.PLAY_TRACK; + } else { + this.spotifyAPICalls.playSongFromUri(this.search.getTrackSearchResults().get(trackNumber).getUri()); + } return this.search.getTrackSearchResults().get(trackNumber); } this.logger.warn(ITME_NOT_FOUND); @@ -172,7 +200,12 @@ public TrackEntity playTrack(int trackNumber) { */ public AlbumEntity playAlbum(int albumNumber) { if (this.search.getAlbumSearchResults().size() > albumNumber) { - this.spotifyAPICalls.playListFromUri(this.search.getAlbumSearchResults().get(albumNumber).getUri()); + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.PLAY_COLLECTION; + this.collectionUriToPlay = this.search.getAlbumSearchResults().get(albumNumber).getUri(); + } else { + this.spotifyAPICalls.playListFromUri(this.search.getAlbumSearchResults().get(albumNumber).getUri()); + } return this.search.getAlbumSearchResults().get(albumNumber); } this.logger.warn(ITME_NOT_FOUND); @@ -188,7 +221,12 @@ public AlbumEntity playAlbum(int albumNumber) { */ public ArtistEntity playArtist(int artistNumber) { if (this.search.getArtistSearchResults().size() > artistNumber) { - this.spotifyAPICalls.playListFromUri(this.search.getArtistSearchResults().get(artistNumber).getUri()); + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.PLAY_COLLECTION; + this.collectionUriToPlay = this.search.getArtistSearchResults().get(artistNumber).getUri(); + } else { + this.spotifyAPICalls.playListFromUri(this.search.getArtistSearchResults().get(artistNumber).getUri()); + } return this.search.getArtistSearchResults().get(artistNumber); } this.logger.warn(ITME_NOT_FOUND); @@ -201,6 +239,10 @@ public ArtistEntity playArtist(int artistNumber) { * @return a boolean. true if the command was executed, else if the command failed */ public boolean resume() { + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.NONE; + return true; + } return this.spotifyAPICalls.resume(); } @@ -214,7 +256,6 @@ public boolean pause() { this.postSuppressionAction = PostSuppressionAction.PAUSE; return true; } - return this.spotifyAPICalls.pause(); } @@ -224,6 +265,10 @@ public boolean pause() { * @return a boolean. true if the command was executed, else if the command failed */ public boolean skip() { + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.SKIP; + return true; + } return this.spotifyAPICalls.skip(); } @@ -233,6 +278,10 @@ public boolean skip() { * @return a boolean. true if the command was executed, else if the command failed */ public boolean back() { + if (this.suppressed) { + this.postSuppressionAction = PostSuppressionAction.BACK; + return true; + } return this.spotifyAPICalls.back(); } @@ -306,10 +355,11 @@ public int getVolume() { } /** - * Suppress music playback temporarily. + * Suppress music playback temporarily and wait with playback start until the unmute is triggered * * @param suppressed - * 'true' to suppress playback, 'false' to restore it + * 'true' to suppress playback or wait with playing a new song or skip a song, 'false' to restore it or + * start playback */ void setSuppressed(boolean suppressed) { if (suppressed != this.suppressed) { @@ -317,17 +367,31 @@ void setSuppressed(boolean suppressed) { boolean isPlaying = this.spotifyAPICalls.getIsPlaying(); if (!suppressed && !isPlaying) { + this.suppressed = false; // Consider resuming playback if (this.postSuppressionAction == PostSuppressionAction.PAUSE) { - // Already paused, do nothing - this.postSuppressionAction = PostSuppressionAction.NONE; + // Nothing to do here. is already paused + } else if (this.postSuppressionAction == PostSuppressionAction.PLAY_TRACK) { + this.spotifyAPICalls.playSongFromUri(this.trackUriToPlay); + } else if (this.postSuppressionAction == PostSuppressionAction.PLAY_COLLECTION) { + this.spotifyAPICalls.playListFromUri(this.collectionUriToPlay); + } else if (this.postSuppressionAction == PostSuppressionAction.BACK) { + back(); + } else if (this.postSuppressionAction == PostSuppressionAction.SKIP) { + skip(); } else { + // resume if was not paused and no other action is executed resume(); } - this.suppressed = false; - } else if (suppressed && isPlaying) { - // Consider pausing playback - pause(); + this.postSuppressionAction = PostSuppressionAction.NONE; + } else if (suppressed) { + if (isPlaying) { + // only pause if playing + pause(); + } else { + // is already paused, may be overwritten from other methods while suppressed + this.postSuppressionAction = PostSuppressionAction.PAUSE; + } this.suppressed = true; } } diff --git a/plugins/spotify/src/main/resources/SpotifySpeech.aim.xml b/plugins/spotify/src/main/resources/SpotifySpeech.aim.xml index 783c2b64f..52dfd90d2 100644 --- a/plugins/spotify/src/main/resources/SpotifySpeech.aim.xml +++ b/plugins/spotify/src/main/resources/SpotifySpeech.aim.xml @@ -2,27 +2,29 @@ - get (music|spotify) devices + (get|list) (music|spotify) devices - get current [playing] (song|track) + (what|get current [playing]) (song|track) [do i listen to] - get [{type}] playlists + (get|list) [{number}] [{type}] playlists (own|featured) + + {amyinteger} + - what type of playlists would you search? - {type} playlist - + Would you like to look for one of your own playlists or a featured one? + {type} [playlist] @@ -33,18 +35,17 @@ - [go] {type} [(a track|the music)] + [go] {type} [(a track|the music)] - (back|skip|pause|pass|resume) + (back|skip|pause|pass|resume|stop) - set [spotify|music] volume [to|on] {volumeoption} - + set [spotify|music] volume [to|on] {volumeoption} (full|max|mute|up|down) @@ -54,8 +55,7 @@ - (set [music] volume [to] |spotify) {volume} percent - + (set [music] volume [to] |spotify) {volume} percent {amyinteger} @@ -65,22 +65,21 @@ - set (spotify|music) device [to {deviceid}] + (set|change) (spotify|music) device [to {deviceid}] {amyinteger} - To which device should i switch? - [device] {deviceid} + To which device should I switch? + [device] {deviceid} - [spotify] play {type} playlist [{songid}] - + [spotify] play [{type}] playlist [{songid}] (own|featured) @@ -88,35 +87,20 @@ {amyinteger} - + + + Would you like to play your own playlist or a featured one? + {type} [playlist] + - which playlist ID would you play? + Which playlist ID do you want me to play? [playlist] {songid} - - - would you play the featured or a own playlist? - - {type} playlist - - - - - play this {mode} [{name}] - - - (track|playlist|album|song|artist) - - - * - - - play following {mode} + play (this|a|the|following) {mode} [{name}] (track|playlist|album|song|artist) @@ -126,23 +110,22 @@ - Which one? + Which one? {name} - - set (spotify|music) device by name + set (spotify|music) device [by] [name] name - To which device should i switch? - [to device] {devicename} + To which device should I switch? + [to device] {devicename} - \ No newline at end of file + diff --git a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICallsTest.java b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICallsTest.java index 7ad92b4f9..5a298fb12 100644 --- a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICallsTest.java +++ b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifyAPICallsTest.java @@ -110,12 +110,19 @@ public void testResume() { } @Test - public void testPause() { + public void testPausePlaying() { doReturn(this.spotifyApi).when(this.spotifyAPICalls).getSpotifyApi(); this.spotifyAPICalls.setCurrentDevice("w"); doReturn(true).when(this.spotifyAPICalls).checkPlayerState(); + doReturn(true).when(this.spotifyAPICalls).getIsPlaying(); assertThat(this.spotifyAPICalls.pause(), equalTo(false)); - doReturn(false).when(this.spotifyAPICalls).checkPlayerState(); + } + @Test + public void testPausePaused() { + doReturn(this.spotifyApi).when(this.spotifyAPICalls).getSpotifyApi(); + this.spotifyAPICalls.setCurrentDevice("w"); + doReturn(true).when(this.spotifyAPICalls).checkPlayerState(); + doReturn(false).when(this.spotifyAPICalls).getIsPlaying(); assertThat(this.spotifyAPICalls.pause(), equalTo(false)); } diff --git a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeechTest.java b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeechTest.java index 85dd25ab2..6018e619b 100644 --- a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeechTest.java +++ b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SpotifySpeechTest.java @@ -102,9 +102,9 @@ void testGetDevices() { void testGetCurrentSong() { when(this.track.toString()).thenReturn("test"); when(this.playerlogic.getCurrentSong()).thenReturn(this.track); - assertThat(this.speech.getCurrentSong(new HashMap<>()), equalTo("track: test")); + assertThat(this.speech.getCurrentSong(new HashMap<>()).getText(), equalTo("track: test")); when(this.playerlogic.getCurrentSong()).thenReturn(null); - assertThat(this.speech.getCurrentSong(new HashMap<>()), equalTo("No song is playing")); + assertThat(this.speech.getCurrentSong(new HashMap<>()).getText(), equalTo("No song is playing")); } @Test @@ -240,7 +240,7 @@ void testSearchASong() { map.put("name", this.songId); when(this.entityData.getString()).thenReturn("track"); map.put("mode", this.entityData); - assertThat(this.speech.searchASong(map), equalTo("Element is not available")); + assertThat(this.speech.searchASong(map).getText(), equalTo("Element is not available")); verify(this.searchLogic).searchforTracks("test", 1); } diff --git a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/DeviceLogicTest.java b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/DeviceLogicTest.java similarity index 97% rename from plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/DeviceLogicTest.java rename to plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/DeviceLogicTest.java index fc3e36a75..9337e5295 100644 --- a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/DeviceLogicTest.java +++ b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/DeviceLogicTest.java @@ -21,7 +21,7 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.plugin.spotify; +package de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -42,6 +42,7 @@ import com.wrapper.spotify.model_objects.specification.PlaylistSimplified; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.SpotifyAPICalls; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.DeviceEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.PlaylistEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic.DeviceLogic; diff --git a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/PlayerLogicTest.java b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogicTest.java similarity index 68% rename from plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/PlayerLogicTest.java rename to plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogicTest.java index 53644b5c5..f66009589 100644 --- a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/PlayerLogicTest.java +++ b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/PlayerLogicTest.java @@ -21,7 +21,7 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.plugin.spotify; +package de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -36,6 +36,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.internal.verification.VerificationModeFactory; import com.wrapper.spotify.model_objects.miscellaneous.CurrentlyPlayingContext; import com.wrapper.spotify.model_objects.miscellaneous.Device; @@ -48,6 +51,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; +import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.SpotifyAPICalls; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.AlbumEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.ArtistEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.PlaylistEntity; @@ -58,11 +62,9 @@ import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; - @ExtendWith(FrameworkExtension.class) class PlayerLogicTest { - private static final String ID1 = "abc123"; private static final String ID2 = "123abc"; private static final String DEVICE_NAME1 = "Hello"; @@ -97,7 +99,7 @@ void init() { this.playerLogic = this.testFramework.setServiceUnderTest(PlayerLogic.class); initDevices(); initCurrentTrack(); - + this.playlistList = new ArrayList<>(); this.playlistList.add(new PlaylistEntity()); @@ -112,7 +114,7 @@ private void initPlaylists() { PlaylistSimplified[] playlistList = new PlaylistSimplified[2]; playlistList[0] = playlist1; playlistList[1] = playlist2; - + PlaylistEntity playlistEntity1 = new PlaylistEntity(); playlistEntity1.setName(PLAYLIST_NAME1); playlistEntity1.setUri(ID1); @@ -122,18 +124,17 @@ private void initPlaylists() { playlistEntity2.setImageUrl(ID1); this.playlistsSpotifyFormat = new Paging.Builder().setItems(playlistList).build(); this.playlistsOwnFormat = new ArrayList<>(); - + this.playlistsOwnFormat.add(playlistEntity1); this.playlistsOwnFormat.add(playlistEntity2); - } private void initDevices() { - devices = new Device[2]; - devices[0] = new Device.Builder().setId(ID1).setIs_active(true).setName(DEVICE_NAME1).setVolume_percent(50) + this.devices = new Device[2]; + this.devices[0] = new Device.Builder().setId(ID1).setIs_active(true).setName(DEVICE_NAME1).setVolume_percent(50) .setType("Smartphone").build(); - devices[1] = new Device.Builder().setId(ID2).setIs_active(false).setName(DEVICE_NAME2).setType("Computer") + this.devices[1] = new Device.Builder().setId(ID2).setIs_active(false).setName(DEVICE_NAME2).setType("Computer") .build(); } @@ -143,7 +144,7 @@ private void initCurrentTrack() { .setArtists(new ArtistSimplified.Builder().setName(ARTIST_NAME1).build(), new ArtistSimplified.Builder().setName(ARTIST_NAME2).build()) .build(); - currentlyPlayingContext = new CurrentlyPlayingContext.Builder().setItem(track1).build(); + this.currentlyPlayingContext = new CurrentlyPlayingContext.Builder().setItem(track1).build(); } @Test @@ -165,10 +166,10 @@ void testInputAuthCode() { this.playerLogic.inputAuthCode(ID2); verify(this.spotifyAPICalls).createRefreshToken(ID2); } - + @Test void testPlayEmptyList() { - when(search.searchFeaturedPlaylists(5)).thenReturn(new ArrayList<>()); + when(this.search.searchFeaturedPlaylists(5)).thenReturn(new ArrayList<>()); assertThat(this.playerLogic.play(), equalTo(null)); } @@ -176,12 +177,24 @@ void testPlayEmptyList() { @Test void testPlayNotEmptyList() { initPlaylists(); - when(this.search.searchFeaturedPlaylists(5)).thenReturn(playlistsOwnFormat); + when(this.search.searchFeaturedPlaylists(5)).thenReturn(this.playlistsOwnFormat); when(this.spotifyAPICalls.playListFromUri(any())).thenReturn(true); - assertThat(this.playerLogic.play().getUri(), equalTo(ID2)); - verify(this.spotifyAPICalls).playListFromUri(ID2); + assertThat(this.playerLogic.play().getUri(), equalTo(ID1)); + verify(this.spotifyAPICalls).playListFromUri(ID1); } - + + @Test + void testPlayNotEmptyListSupressed() { + initPlaylists(); + when(this.search.searchFeaturedPlaylists(5)).thenReturn(this.playlistsOwnFormat); + when(this.spotifyAPICalls.playListFromUri(any())).thenReturn(true); + this.playerLogic.setSuppressed(true); + assertThat(this.playerLogic.play().getUri(), equalTo(ID1)); + verify(this.spotifyAPICalls, Mockito.never()).playListFromUri(any()); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).playListFromUri(any()); + } + @Test void playPlaylist() { when(this.search.getFeaturedPlaylists()).thenReturn(this.playlistList); @@ -189,20 +202,31 @@ void playPlaylist() { assertThat(this.playerLogic.playPlaylist(1, SearchTypes.FEATURED_PLAYLISTS), equalTo(null)); } + @Test + void playPlaylistSupressed() { + when(this.search.getFeaturedPlaylists()).thenReturn(this.playlistList); + this.playerLogic.setSuppressed(true); + assertThat(this.playerLogic.playPlaylist(0, SearchTypes.FEATURED_PLAYLISTS), equalTo(this.playlistList.get(0))); + assertThat(this.playerLogic.playPlaylist(1, SearchTypes.FEATURED_PLAYLISTS), equalTo(null)); + verify(this.spotifyAPICalls, Mockito.never()).playListFromUri(any()); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).playListFromUri(any()); + } + @Test void playPlaylistUser() { when(this.search.getOwnPlaylists()).thenReturn(this.playlistList); assertThat(this.playerLogic.playPlaylist(0, SearchTypes.USER_PLAYLISTS), equalTo(this.playlistList.get(0))); assertThat(this.playerLogic.playPlaylist(1, SearchTypes.USER_PLAYLISTS), equalTo(null)); } - + @Test void playPlaylistSearch() { when(this.search.getPlaylistSearchResults()).thenReturn(this.playlistList); assertThat(this.playerLogic.playPlaylist(0, SearchTypes.SEARCH_PLAYLISTS), equalTo(this.playlistList.get(0))); assertThat(this.playerLogic.playPlaylist(1, SearchTypes.SEARCH_PLAYLISTS), equalTo(null)); } - + @Test void playTrackSearch() { List trackList = new ArrayList<>(); @@ -211,7 +235,20 @@ void playTrackSearch() { assertThat(this.playerLogic.playTrack(0), equalTo(trackList.get(0))); assertThat(this.playerLogic.playTrack(1), equalTo(null)); } - + + @Test + void playTrackSearchSupressed() { + List trackList = new ArrayList<>(); + trackList.add(new TrackEntity()); + when(this.search.getTrackSearchResults()).thenReturn(trackList); + this.playerLogic.setSuppressed(true); + assertThat(this.playerLogic.playTrack(0), equalTo(trackList.get(0))); + assertThat(this.playerLogic.playTrack(1), equalTo(null)); + verify(this.spotifyAPICalls, Mockito.never()).playListFromUri(any()); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).playSongFromUri(any()); + } + @Test void playAlbumSearch() { List albumList = new ArrayList<>(); @@ -220,7 +257,20 @@ void playAlbumSearch() { assertThat(this.playerLogic.playAlbum(0), equalTo(albumList.get(0))); assertThat(this.playerLogic.playAlbum(1), equalTo(null)); } - + + @Test + void playAlbumSearchSupressed() { + List albumList = new ArrayList<>(); + albumList.add(new AlbumEntity()); + when(this.search.getAlbumSearchResults()).thenReturn(albumList); + this.playerLogic.setSuppressed(true); + assertThat(this.playerLogic.playAlbum(0), equalTo(albumList.get(0))); + assertThat(this.playerLogic.playAlbum(1), equalTo(null)); + verify(this.spotifyAPICalls, Mockito.never()).playListFromUri(any()); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).playListFromUri(any()); + } + @Test void playArtistSearch() { List artistList = new ArrayList<>(); @@ -230,30 +280,81 @@ void playArtistSearch() { assertThat(this.playerLogic.playArtist(1), equalTo(null)); } + @Test + void playArtistSearchSupressed() { + List artistList = new ArrayList<>(); + artistList.add(new ArtistEntity()); + when(this.search.getArtistSearchResults()).thenReturn(artistList); + this.playerLogic.setSuppressed(true); + assertThat(this.playerLogic.playArtist(0), equalTo(artistList.get(0))); + assertThat(this.playerLogic.playArtist(1), equalTo(null)); + verify(this.spotifyAPICalls, Mockito.never()).playListFromUri(any()); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).playListFromUri(any()); + } + @Test void testResume() { this.playerLogic.resume(); verify(this.spotifyAPICalls).resume(); } + @Test + void testSupressedResumep() { + this.playerLogic.setSuppressed(true); + this.playerLogic.resume(); + verify(this.spotifyAPICalls, Mockito.never()).resume(); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).resume(); + } + @Test void testPause() { this.playerLogic.pause(); verify(this.spotifyAPICalls).pause(); } + @Test + void testSupressedPause() { + when(this.spotifyAPICalls.getIsPlaying()).thenReturn(true); + this.playerLogic.setSuppressed(true); + verify(this.spotifyAPICalls).pause(); + this.playerLogic.pause(); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls, Mockito.atMost(1)).pause(); + + } + @Test void testSkip() { this.playerLogic.skip(); verify(this.spotifyAPICalls).skip(); } + @Test + void testSupressedSkip() { + this.playerLogic.setSuppressed(true); + this.playerLogic.skip(); + verify(this.spotifyAPICalls, Mockito.never()).skip(); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).skip(); + } + @Test void testBack() { this.playerLogic.back(); verify(this.spotifyAPICalls).back(); } + @Test + void testSupressedBack() { + this.playerLogic.setSuppressed(true); + this.playerLogic.back(); + verify(this.spotifyAPICalls, Mockito.never()).back(); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).back(); + } + @Test void testSetVolumeStringMute() { when(this.spotifyAPICalls.getVolume()).thenReturn(50); @@ -330,7 +431,7 @@ void testVolumeInt() { assertThat(this.playerLogic.setVolume(1), equalTo(1)); verify(this.spotifyAPICalls).setVolume(1); } - + @Test void testCurrentSong() { when(this.spotifyAPICalls.getCurrentPlayingContext()).thenReturn(this.currentlyPlayingContext); @@ -338,9 +439,30 @@ void testCurrentSong() { TrackEntity track = this.playerLogic.getCurrentSong(); assertThat(track.getUri(), equalTo(ID1)); } + @Test void testNoCurrentSong() { when(this.spotifyAPICalls.getCurrentPlayingContext()).thenReturn(null); assertThat(this.playerLogic.getCurrentSong(), equalTo(null)); } + + @Test + void testSuppresedPlayingNothingChanged() { + when(this.spotifyAPICalls.getIsPlaying()).thenReturn(true); + this.playerLogic.setSuppressed(true); + verify(this.spotifyAPICalls).pause(); + when(this.spotifyAPICalls.getIsPlaying()).thenReturn(false); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls).resume(); + } + + @Test + void testSuppresedNotPlayingNothingChanged() { + when(this.spotifyAPICalls.getIsPlaying()).thenReturn(false); + this.playerLogic.setSuppressed(true); + verify(this.spotifyAPICalls, Mockito.never()).pause(); + this.playerLogic.setSuppressed(false); + verify(this.spotifyAPICalls, Mockito.never()).resume(); + } + } diff --git a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SearchTest.java b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/SearchTest.java similarity index 99% rename from plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SearchTest.java rename to plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/SearchTest.java index 07352dddc..ee9d87c8a 100644 --- a/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/SearchTest.java +++ b/plugins/spotify/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/spotify/logic/SearchTest.java @@ -21,7 +21,7 @@ * For more information see notice.md */ -package de.unistuttgart.iaas.amyassist.amy.plugin.spotify; +package de.unistuttgart.iaas.amyassist.amy.plugin.spotify.logic; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -49,6 +49,7 @@ import com.wrapper.spotify.model_objects.specification.User; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.SpotifyAPICalls; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.AlbumEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.ArtistEntity; import de.unistuttgart.iaas.amyassist.amy.plugin.spotify.entities.PlaylistEntity; diff --git a/plugins/systemtime/pom.xml b/plugins/systemtime/pom.xml index c83a3ff74..fc1efebe0 100644 --- a/plugins/systemtime/pom.xml +++ b/plugins/systemtime/pom.xml @@ -8,7 +8,13 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml + + + org.junit.jupiter + junit-jupiter-params + + diff --git a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogic.java b/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogic.java deleted file mode 100644 index 367f8f910..000000000 --- a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogic.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; - -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.TextStyle; -import java.util.Locale; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; -import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; - -/** - * The Logic for the system time - * - * @author Florian Bauer, Patrick Gebhardt - */ -@Service -public class SystemTimeLogic { - - @Reference - private Environment environment; - - /** - * - * @return object of type Date - */ - public LocalDateTime getTimeStamp() { - return this.environment.getCurrentLocalDateTime(); - } - - /** - * - * @return current year, e.g. 2018 - */ - public int getYear() { - return this.getTimeStamp().getYear(); - } - - /** - * - * @return current date as String, e.g. "1st of april" - */ - public String getDate() { - LocalDate localDate = this.getTimeStamp().toLocalDate(); - return ordinal(localDate.getDayOfMonth()) + " of " - + localDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); - } - - /** - * - * @return current time as String (HH:mm:ss), e.g. 12:45:20 - */ - public String getTime() { - return this.getTimeStamp().toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm:ss")); - } - - /** - * A method to convert the integer day to an ordinal (from 1 to 31) - * - * @param i - * the day as integer - * @return the day as ordinal, e.g. 1st - */ - public static String ordinal(int i) { - String[] ordinals = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; - if (10 < i && i < 14) { - return i + "th"; - } - return i + ordinals[i % 10]; - } -} diff --git a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeResource.java b/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeResource.java deleted file mode 100644 index abdc7d7a6..000000000 --- a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeResource.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; - -import java.time.LocalDateTime; - -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.utility.rest.Resource; -import de.unistuttgart.iaas.amyassist.amy.utility.rest.ResourceEntity; - -/** - * REST Resource for the system time - * - * @author Muhammed Kaya - */ -@Path(SystemTimeResource.PATH) -public class SystemTimeResource implements Resource { - - /** - * the resource path for this plugin - */ - public static final String PATH = "systemtime"; - - @Reference - private SystemTimeLogic logic; - - /** - * get the current LocalDatetime as object - * - * @return object of type Date - */ - @GET - @Path("timeStamp") - @Produces(MediaType.TEXT_PLAIN) - public LocalDateTime getTimeStamp() { - return this.logic.getTimeStamp(); - } - - /** - *get the current system year - * - * @return current year, e.g. 2018 - */ - @GET - @Path("year") - @Produces(MediaType.TEXT_PLAIN) - public int getYear() { - return this.logic.getYear(); - } - - /** - * get the current system time - * - * @return current time (hour minute second) in a string - */ - @GET - @Path("time") - @Produces(MediaType.TEXT_PLAIN) - public String getTime() { - return this.logic.getTime(); - } - - /** - * get the current system date - * - * @return current date (day month year) in a string - */ - @GET - @Path("date") - @Produces(MediaType.TEXT_PLAIN) - public String getDate() { - return this.logic.getDate(); - } - - /** - * @see de.unistuttgart.iaas.amyassist.amy.utility.rest.Resource#getPluginDescripion() - */ - @Override - public ResourceEntity getPluginDescripion() { - return null; - } - -} diff --git a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeech.java b/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeech.java index 5eaa29288..970cc7d99 100644 --- a/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeech.java +++ b/plugins/systemtime/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeech.java @@ -23,17 +23,23 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; +import java.time.Duration; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.TextStyle; +import java.util.Locale; import java.util.Map; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; /** * A plugin which tells time and date - * + * * @author Florian Bauer, Patrick Gebhardt */ @Service @@ -41,39 +47,126 @@ public class SystemTimeSpeech { @Reference - private SystemTimeLogic logic; + private Environment environment; + + private static final String ITIS = "It is "; /** * A method which returns the current time * - * @return current time (hour minute) in a string, e.g. it is 10:30 + * @param entities + * from the speech + * @return current time (hour minute) in a string, e.g. It is 10:30 */ @Intent() public String time(Map entities) { - if (this.logic.getTime() != null && this.logic.getTime().length() > 4) { - return "it is " + this.logic.getTime().substring(0, 5); - } - return "couldn't find correct time, this is what I found: " + this.logic.getTime(); + return ITIS + + this.environment.getCurrentLocalDateTime().toLocalTime().format(DateTimeFormatter.ofPattern("HH:mm")) + + "."; } /** * A method which returns the current date * - * @return current date (day month year) in a string, e.g. it is the 20th of june + * @param entities + * from the speech + * @return current date (day month year) in a string, e.g. It is the 20th of june. */ @Intent() public String date(Map entities) { - return "it is the " + this.logic.getDate(); + return ITIS + "the " + ordinal(this.environment.getCurrentLocalDateTime().getDayOfMonth()) + " of " + + this.environment.getCurrentLocalDateTime().getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + + "."; } /** * A method which returns the current year - * - * @return current year in a string, e.g. it is 2018 + * + * @param entities + * from the speech + * @return current year in a string, e.g. It is 2018. */ @Intent public String year(Map entities) { - return "it is " + this.logic.getYear(); + return ITIS + this.environment.getCurrentLocalDateTime().getYear() + "."; + } + + /** + * A method which returns the day of week of a chosen date. + * + * @param entities + * from the speech + * @return current year in a string, e.g. Tomorrow is monday. + */ + @Intent + public String dayOfWeek(Map entities) { + LocalDate currentDate = this.environment.getCurrentLocalDateTime().toLocalDate(); + LocalDate chosenDate = entities.get("date").getDate(); + if (entities.get("date").getString().equals("today")) { + return "Today is " + chosenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + "."; + } + if (entities.get("date").getString().equals("tomorrow")) { + return "Tomorrow is " + chosenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + "."; + } + if (entities.get("year") == null && chosenDate.isBefore(currentDate)) { + chosenDate = chosenDate.withYear(currentDate.getYear() + 1); + } + String tense = " is a "; + if (chosenDate.isBefore(currentDate)) { + tense = " was a "; + } + if (chosenDate.getYear() != this.environment.getCurrentLocalDateTime().getYear()) { + return "The " + ordinal(chosenDate.getDayOfMonth()) + " of " + + chosenDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + " " + chosenDate.getYear() + + tense + chosenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + "."; + } + return "The " + ordinal(chosenDate.getDayOfMonth()) + " of " + + chosenDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + tense + + chosenDate.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + "."; + } + + /** + * A method which returns the day count until or since a date in comparison to now + * + * @param entities + * from the speech + * @return current year in a string, e.g. 1 day until tomorrow. + */ + @Intent + public String howManyDays(Map entities) { + LocalDate chosenDate = entities.get("date").getDate(); + LocalDate currentDate = this.environment.getCurrentLocalDateTime().toLocalDate(); + String day = " days "; + if (entities.get("time").getString().equals("until") && entities.get("year") == null + && chosenDate.isBefore(currentDate)) { + chosenDate = chosenDate.withYear(currentDate.getYear() + 1); + } + long difference = Math.abs(Duration.between(chosenDate.atStartOfDay(), currentDate.atStartOfDay()).toDays()); + if (difference == 1) { + day = " day "; + } + if (chosenDate.isBefore(currentDate)) { + return difference + day + " have passed since the " + ordinal(chosenDate.getDayOfMonth()) + " of " + + chosenDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) + " " + + chosenDate.getYear() + "."; + } + return difference + day + " until the " + ordinal(chosenDate.getDayOfMonth()) + " of " + chosenDate.getMonth() + .getDisplayName(TextStyle.FULL, Locale.ENGLISH) + " " + chosenDate.getYear() + "."; + } + + /** + * A method to convert the integer day to an ordinal (from 1 to 31) + * + * @param i + * the day as integer + * @return the day as ordinal, e.g. 1st + */ + public static String ordinal(int i) { + String[] ordinals = { "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th" }; + if (10 < i && i < 14) { + return i + "th"; + } + return i + ordinals[i % 10]; } } diff --git a/plugins/systemtime/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/systemtime/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services deleted file mode 100644 index b9c958243..000000000 --- a/plugins/systemtime/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services +++ /dev/null @@ -1 +0,0 @@ -de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeLogic diff --git a/plugins/systemtime/src/main/resources/META-INF/javax.ws.rs.Path b/plugins/systemtime/src/main/resources/META-INF/javax.ws.rs.Path deleted file mode 100644 index f9a4aa7d6..000000000 --- a/plugins/systemtime/src/main/resources/META-INF/javax.ws.rs.Path +++ /dev/null @@ -1 +0,0 @@ -de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeResource diff --git a/plugins/systemtime/src/main/resources/SystemTimeSpeech.aim.xml b/plugins/systemtime/src/main/resources/SystemTimeSpeech.aim.xml index 50ff5e785..a042255d9 100644 --- a/plugins/systemtime/src/main/resources/SystemTimeSpeech.aim.xml +++ b/plugins/systemtime/src/main/resources/SystemTimeSpeech.aim.xml @@ -2,15 +2,49 @@ - (what is|tell me) the time + [what|tell me] [is] [the] time [is it] - (what is|tell me) the date + [what|tell me] [is] [the] (date|day) [is] [today] - (what is|tell me) the year + [what|tell me] [is] [the] year [do we have|is it|are we living in] - - \ No newline at end of file + + [what|tell me the] (week day|day [of the] week) [do we have|is|was] [the] [{date}] + + + {amyyear} + + + ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{year}]|tomorrow|today) + + + + At which date? + {date} + + + + [How many] days [have passed] {time} [the {date}] + + + (until|since) + + + {amyyear} + + + ([{amydayofweek} [the]] {amydayofmonth} [of] {amymonth} [{year}]|tomorrow|today) + + + + Which date? + {date} + + + diff --git a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogicTest.java b/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogicTest.java deleted file mode 100644 index 2888e7d63..000000000 --- a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeLogicTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; - -import static org.hamcrest.MatcherAssert.*; -import static org.hamcrest.Matchers.*; - -import java.time.LocalDateTime; -import java.util.Calendar; -import java.util.Date; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; -import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; -import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; - -/** - * TODO: Description - * - * @author Leon Kiefer, Florian Bauer - */ -@ExtendWith(FrameworkExtension.class) -class SystemTimeLogicTest { - - @Reference - private TestFramework framework; - - private SystemTimeLogic systemTimeLogic; - private Environment environment; - - @BeforeEach - public void setup() { - this.environment = this.framework.mockService(Environment.class); - this.systemTimeLogic = this.framework.setServiceUnderTest(SystemTimeLogic.class); - } - - @Test - void test() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-05-29T08:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getTimeStamp(), is(localDateTime)); - } - - @Test - void test4() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-05-29T08:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getYear(), equalTo(2015)); - - } - - @Test - void test5() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-05-29T08:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getDate(), equalTo("29th of May")); - } - - @Test - void test9() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-05-29T08:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getTime(), equalTo("08:00:00")); - } - - @Test - void test8() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-06-01T00:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getDate(), equalTo("1st of June")); - } - - @Test - void test7() { - LocalDateTime localDateTime = LocalDateTime.parse("2015-06-12T12:00:00"); - Mockito.doReturn(localDateTime).when(this.environment).getCurrentLocalDateTime(); - - assertThat(this.systemTimeLogic.getDate(), equalTo("12th of June")); - } - -} diff --git a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeRestTest.java b/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeRestTest.java deleted file mode 100644 index c8107bf9d..000000000 --- a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeRestTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * This source file is part of the Amy open source project. - * For more information see github.com/AmyAssist - * - * Copyright (c) 2018 the Amy project authors. - * - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * For more information see notice.md - */ - -package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.time.format.TextStyle; -import java.util.Locale; - -import static de.unistuttgart.iaas.amyassist.amy.test.matcher.rest.ResponseMatchers.status; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.equalTo; - -import javax.ws.rs.client.WebTarget; -import javax.ws.rs.core.Response; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mockito; - -import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; -import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; - -/** - * test for the rest resource of the system time - * - * @author Muhammed Kaya - */ -@ExtendWith(FrameworkExtension.class) -class SystemTimeRestTest { - - @Reference - private TestFramework testFramework; - - private SystemTimeLogic logic; - - private WebTarget target; - - private LocalDateTime ldt; - - /** - * setUp - */ - @BeforeEach - public void setUp() { - this.target = this.testFramework.setRESTResource(SystemTimeResource.class); - this.logic = this.testFramework.mockService(SystemTimeLogic.class); - - this.ldt = LocalDateTime.parse("2015-05-29T08:00:00"); - } - - /** - * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeResource#getTimeStamp()}. - */ - @Test - void testGetTimeStamp() { - Mockito.when(this.logic.getTimeStamp()).thenReturn(this.ldt); - try (Response response = this.target.path("timeStamp").request().get()) { - assertThat(response.readEntity(String.class), equalTo(this.ldt.toString())); - assertThat(response, status(200)); - Mockito.verify(this.logic).getTimeStamp(); - } - } - - /** - * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeResource#getYear()()}. - */ - @Test - void testGetYear() { - Mockito.when(this.logic.getYear()).thenReturn(this.ldt.getYear()); - try (Response response = this.target.path("year").request().get()) { - assertThat(response.readEntity(int.class), is(this.ldt.getYear())); - assertThat(response, status(200)); - Mockito.verify(this.logic).getYear(); - } - } - - /** - * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeResource#getTime()}. - */ - @Test - void testGetTime() { - Mockito.when(this.logic.getTime()).thenReturn(this.ldt.format(DateTimeFormatter.ofPattern("HH:mm:ss"))); - try (Response response = this.target.path("time").request().get()) { - assertThat( - response.readEntity(String.class), equalTo(this.ldt.format(DateTimeFormatter.ofPattern("HH:mm:ss")))); - assertThat(response, status(200)); - Mockito.verify(this.logic).getTime(); - } - } - - /** - * Test method for {@link de.unistuttgart.iaas.amyassist.amy.plugin.systemtime.SystemTimeResource#getDate()}. - */ - @Test - void testGetDate() { - this.ldt.toLocalDate(); - String date = this.ldt.getDayOfMonth() + " of " - + this.ldt.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH); - Mockito.when(this.logic.getDate()).thenReturn(date); - try (Response response = this.target.path("date").request().get()) { - assertThat(response.readEntity(String.class), equalTo(date.toString())); - assertThat(response, status(200)); - Mockito.verify(this.logic).getDate(); - } - } - -} diff --git a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeechTest.java b/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeechTest.java index 94677533c..9eb29cf7d 100644 --- a/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeechTest.java +++ b/plugins/systemtime/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/systemtime/SystemTimeSpeechTest.java @@ -23,63 +23,147 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.systemtime; -import javax.annotation.meta.When; +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.HashMap; +import java.util.Map; +import java.util.stream.Stream; + +import org.apache.commons.lang3.tuple.Pair; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.internal.verification.NoMoreInteractions; -import static org.mockito.Mockito.times; -import org.mockito.junit.jupiter.MockitoExtension; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import java.util.HashMap; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; /** * test class for system Time speech - * @author Lars Buttgereit + * + * @author Lars Buttgereit, Florian Bauer */ -@ExtendWith({ MockitoExtension.class, FrameworkExtension.class }) +@ExtendWith(FrameworkExtension.class) public class SystemTimeSpeechTest { @Reference private TestFramework testFramework; - - private SystemTimeLogic logic; + + private Environment environment; private SystemTimeSpeech speech; - + + private LocalDateTime ldt; + + /** + * initialize the test + */ @BeforeEach void init() { - this.logic = this.testFramework.mockService(SystemTimeLogic.class); + this.environment = this.testFramework.mockService(Environment.class); this.speech = this.testFramework.setServiceUnderTest(SystemTimeSpeech.class); + this.ldt = LocalDateTime.of(2018, 8, 20, 20, 15, 28); } - + + /** + * Test time(Map) + */ @Test void testTime() { - when(this.logic.getTime()).thenReturn("20:20"); - this.speech.time(new HashMap<>()); - verify(this.logic, times(3)).getTime(); - when(this.logic.getTime()).thenReturn(null); - this.speech.time(new HashMap<>()); - verify(this.logic, times(5)).getTime(); + when(this.environment.getCurrentLocalDateTime()).thenReturn(this.ldt); + assertThat(this.speech.time(new HashMap<>()), equalToIgnoringWhiteSpace("It is 20:15.")); } - + + /** + * Test date(Map) + */ @Test void testDate() { - this.speech.date(new HashMap<>()); - verify(this.logic).getDate(); + when(this.environment.getCurrentLocalDateTime()).thenReturn(this.ldt); + assertThat(this.speech.date(new HashMap<>()), equalToIgnoringWhiteSpace("It is the 20th of august.")); } - + + /** + * Test year(Map) + */ @Test void testYear() { - this.speech.year(new HashMap<>()); - verify(this.logic).getYear(); + when(this.environment.getCurrentLocalDateTime()).thenReturn(this.ldt); + assertThat(this.speech.year(new HashMap<>()), equalToIgnoringWhiteSpace("It is 2018.")); + } + + /** + * @param testCase + * a combination of the input variables and the expected outcome + */ + @ParameterizedTest + @MethodSource("dayOfWeekPairs") + public void testDayOfWeek(Pair, String> testCase) { + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 20, 00, 00)); + String setEvent = this.speech.dayOfWeek(testCase.getLeft()); + assertThat(setEvent, equalToIgnoringWhiteSpace(testCase.getRight())); + } + + /** + * + * @return the test cases used in the {@link #testDayOfWeek(Pair)} test + */ + public static Stream, String>> dayOfWeekPairs() { + return Stream.of(Pair.of(speechMap("", LocalDate.of(2018, 9, 24), "today", ""), "Today is monday."), + Pair.of(speechMap("", LocalDate.of(2018, 9, 25), "tomorrow", "2018"), "Tomorrow is tuesday."), + Pair.of(speechMap("", LocalDate.of(2019, 1, 2), "", "2019"), "The 2nd of january 2019 is a wednesday."), + Pair.of(speechMap("", LocalDate.of(2018, 4, 11), "", ""), "The 11th of april 2019 is a thursday."), + Pair.of(speechMap("", LocalDate.of(2018, 4, 13), "", "2018"), "The 13th of april was a friday."), + Pair.of(speechMap("", LocalDate.of(2018, 8, 18), "", "2018"), "The 18th of august was a saturday."), + Pair.of(speechMap("", LocalDate.of(2017, 12, 24), "", "2017"), "The 24th of december 2017 was a sunday.")); + } + + private static Map speechMap(String timeString, LocalDate localDate, String dateString, String yearString) { + EntityData date = Mockito.mock(EntityData.class); + EntityData year = Mockito.mock(EntityData.class); + EntityData time = Mockito.mock(EntityData.class); + Map map = new HashMap<>(); + when(time.getString()).thenReturn(timeString); + map.put("time", time); + when(date.getDate()).thenReturn(localDate); + when(date.getString()).thenReturn(dateString); + map.put("date", date); + if (yearString.isEmpty()) { + map.put("year", null); + } else { + map.put("year", year); + } + return map; + } + + /** + * @param testCase + * a combination of the input variables and the expected outcome + */ + @ParameterizedTest + @MethodSource("howManyDaysPairs") + public void testHowManyDays(Pair, String> testCase) { + when(this.environment.getCurrentLocalDateTime()).thenReturn(LocalDateTime.of(2018, 9, 24, 00, 00)); + String setEvent = this.speech.howManyDays(testCase.getLeft()); + assertThat(setEvent, equalToIgnoringWhiteSpace(testCase.getRight())); + } + + /** + * + * @return the test cases used in the {@link #testHowManyDays(Pair)} test + */ + public static Stream, String>> howManyDaysPairs() { + return Stream.of(Pair.of(speechMap("until", LocalDate.of(2018, 9, 24), "today", ""), "0 days until the 24th of september 2018."), + Pair.of(speechMap("until", LocalDate.of(2018, 9, 25), "tomorrow", "2018"), "1 day until the 25th of september 2018."), + Pair.of(speechMap("until", LocalDate.of(2018, 9, 19), "", ""), "360 days until the 19th of september 2019."), + Pair.of(speechMap("since", LocalDate.of(2018, 9, 10), "", "2018"), "14 days have passed since the 10th of september 2018.")); } } diff --git a/plugins/timer/.settings/org.eclipse.jdt.core.prefs b/plugins/timer/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/plugins/timer/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/plugins/timer/.settings/org.eclipse.jdt.ui.prefs b/plugins/timer/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/plugins/timer/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/plugins/timer/.settings/org.eclipse.m2e.core.prefs b/plugins/timer/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/timer/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/timer/pom.xml b/plugins/timer/pom.xml new file mode 100644 index 000000000..24f9fbdff --- /dev/null +++ b/plugins/timer/pom.xml @@ -0,0 +1,14 @@ + + 4.0.0 + amy-plugin-timer + Amy plugin timer + The Amy timer plugin + + de.unistuttgart.iaas.amyassist + amy-plugin + 0.6.0 + ../pom.xml + + diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/Timer.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/Timer.java new file mode 100644 index 000000000..61a9f11ca --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/Timer.java @@ -0,0 +1,190 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +import javax.persistence.*; +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; + +import de.unistuttgart.iaas.amyassist.amy.registry.RegistryEntity; +import de.unistuttgart.iaas.amyassist.amy.utility.rest.adapter.DurationAdapter; +import de.unistuttgart.iaas.amyassist.amy.utility.rest.adapter.LocalDateTimeAdapter; + +/** + * Class that defines timer attributes and behaviour + * + * @author Patrick Singer, Patrick Gebhardt, Florian Bauer + * + */ +@Entity +@PersistenceUnit(unitName = "TimerRegistry") +public class Timer extends de.unistuttgart.iaas.amyassist.amy.utility.rest.Entity implements RegistryEntity { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(updatable = false, nullable = false) + private int persistentId; + private int id; + @XmlJavaTypeAdapter(LocalDateTimeAdapter.class) + private LocalDateTime timerTime; + @XmlJavaTypeAdapter(DurationAdapter.class) + private Duration remainingTime; + private boolean active; + + /** + * Default constructor + */ + public Timer() { + + } + + /** + * Constructor for a timer object + * + * @param id + * id of the timer + * @param timerTime + * date and time of the timer + * @param remainingTime + * time until the timer rings + * @param active + * states whether the timer is active or inactive + */ + public Timer(int id, LocalDateTime timerTime, Duration remainingTime, boolean active) { + if (id < 1) + throw new IllegalArgumentException(); + + this.id = id; + this.timerTime = timerTime; + this.remainingTime = remainingTime; + this.active = active; + } + + /** + * Returns a string representation of this object + * + * @see java.lang.Object#toString() + * + * @return String representation of this timer object + */ + @Override + public String toString() { + return this.id + ":" + this.timerTime.getHour() + ":" + this.timerTime.getMinute() + ":" + + this.timerTime.getSecond(); + } + + /** + * Returns this timers delay until it goes off + * + * @return the remaining time in seconds + */ + public Duration getRemainingTime() { + if (this.active) { + LocalDateTime current = LocalDateTime.now(); + LocalDateTime future = this.getTimerTime(); + return (Duration.ofMillis(current.until(future, ChronoUnit.MILLIS))); + } + return this.remainingTime; + } + + /** + * @return id + */ + public int getId() { + return this.id; + } + + /** + * @param id + * timer id + */ + public void setId(int id) { + this.id = id; + } + + /** + * @return whether the timer is active or not + */ + public boolean isActive() { + return this.active; + } + + /** + * @param active + * the value whether the timer is active or not + */ + public void setActive(boolean active) { + this.active = active; + } + + /** + * @return timerDate + */ + public LocalDateTime getTimerTime() { + return this.timerTime; + } + + /** + * @param timerTime + * date and time the timer rings + */ + public void setTimerTime(LocalDateTime timerTime) { + this.timerTime = timerTime; + } + + /** + * @param remainingTime + * sets the remainingTime of the timer + */ + public void duration(Duration remainingTime) { + this.remainingTime = remainingTime; + } + + /** + * @return the duration of the timer + */ + public Duration duration() { + return this.remainingTime; + } + + /** + * @param persistentId + * persistentId + */ + public void setPersistentId(int persistentId) { + this.persistentId = persistentId; + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.registry.RegistryEntity#getPersistentId() + */ + @Override + public int getPersistentId() { + return this.persistentId; + } + +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepService.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepService.java new file mode 100644 index 000000000..5828c89e8 --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepService.java @@ -0,0 +1,152 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.HashSet; +import java.util.Set; + +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.UnsupportedAudioFileException; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.audio.AudioManager; +import de.unistuttgart.iaas.amyassist.amy.core.audio.LocalAudio; +import de.unistuttgart.iaas.amyassist.amy.core.audio.sound.Sound; +import de.unistuttgart.iaas.amyassist.amy.core.audio.sound.SoundFactory; +import de.unistuttgart.iaas.amyassist.amy.core.audio.sound.SoundPlayer; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PreDestroy; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; + +/** + * This class controls the alarm sound file, which is used for the alarm clock + * + * @author Patrick Gebhardt, Tim Neumann + */ +@Service +public class TimerBeepService { + + private static final String ALARMSOUND = "alarmsound.wav"; + + @Reference + private Logger logger; + @Reference + private Environment env; + + @Reference + private SoundFactory sf; + + @Reference + private AudioManager am; + + @Reference + private LocalAudio la; + + @Reference + private MessageHub messageHub; + + private Sound beepSound; + private SoundPlayer beepPlayer; + private Set alarmList = new HashSet<>(); + private Set timerList = new HashSet<>(); + + @PostConstruct + + private void init() { + InputStream resolve = this.getClass().getResourceAsStream(ALARMSOUND); + try (InputStream bufferedIn = new BufferedInputStream(resolve); + AudioInputStream soundIn = AudioSystem.getAudioInputStream(bufferedIn);) { + this.beepSound = this.sf.loadSound(soundIn); + } catch (IOException | UnsupportedAudioFileException e) { + this.logger.error("Cant load alarm sound", e); + } + } + + /** + * @param timer + * timer from the timer class + * @return returns the list of timers + */ + public Set beep(Timer timer) { + this.timerList.add(timer.getId()); + this.update(); + return this.timerList; + } + + /** + * @param timer + * timer from the timer class + * @return returns the list of timers + */ + public Set stopBeep(Timer timer) { + this.timerList.remove(timer.getId()); + this.update(); + return this.timerList; + } + + private void update() { + + if (!this.alarmList.isEmpty() || !this.timerList.isEmpty()) { + this.startBeeping(); + } else { + this.stopBeeping(); + } + } + + /** + * Starts the audio file, when the timer / alarm should ring and sets the beeping variable on true + */ + private void startBeeping() { + if (this.beepPlayer == null || !this.beepPlayer.isRunning()) { + this.beepPlayer = this.beepSound.getInfiniteLoopPlayer(); + if (this.la.isLocalAudioAvailable()) { + this.am.playAudio(this.la.getLocalAudioEnvironmentIdentifier(), this.beepPlayer.getAudioStream(), + AudioManager.OutputBehavior.SUSPEND); + } + this.beepPlayer.start(); + } + } + + /** + * Stops the audio file, when the timer / alarm is deactivated and sets the beeping variable on false + */ + void stopBeeping() { + if (this.beepPlayer != null && this.beepPlayer.isRunning()) { + this.beepPlayer.stop(); + } + } + + @PreDestroy + private void preDestroy() { + this.stopBeeping(); + } +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogic.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogic.java new file mode 100644 index 000000000..5ab5c9448 --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogic.java @@ -0,0 +1,262 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.ZonedDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.NoSuchElementException; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService; +import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.Topics; + +/** + * This class implements the logic for all the functions that our timer is capable of + * + * @author Patrick Gebhardt + */ + +@Service(TimerLogic.class) +public class TimerLogic implements RunnableService { + + @Reference + private TimerBeepService timerbeep; + + @Reference + private TimerRegistry timerStorage; + + @Reference + private TaskScheduler taskScheduler; + + @Reference + private Environment environment; + + private String timerS = "Timer "; + + @Reference + private MessageHub messageHub; + + /** + * Creates a Runnable that plays the alarm sound. License: Attribution 3.0 + * http://creativecommons.org/licenses/by-sa/3.0/deed.de Recorded by Daniel Simion + * + * @param timer + * timer which should ring + * + * @return runnable + * + */ + public Runnable createTimerRunnable(Timer timer) { + return () -> { + Timer currenttimer = this.getTimer(timer.getId()); + if (currenttimer.isActive() + && (currenttimer.getRemainingTime().isNegative() || currenttimer.getRemainingTime().isZero())) { + String topic = Topics.smarthomeAll("timer"); + this.messageHub.publish(topic, "Timer " + currenttimer.getId() + "is ringing"); + this.timerbeep.beep(currenttimer); + } + }; + + } + + /** + * Sets new timer and schedules it + * + * @param timerTime + * date and time of the timer + * @return the new timer + */ + public Timer setTimer(LocalDateTime timerTime) { + int id = searchTimerId(); + Timer timer = new Timer(id, timerTime, null, true); + this.timerStorage.save(timer); + Runnable timerRunnable = createTimerRunnable(timer); + ZonedDateTime with = this.environment.getCurrentDateTime().with(timer.getTimerTime()); + if (with.isBefore(this.environment.getCurrentDateTime())) { + with = with.plusDays(1); + } + this.taskScheduler.schedule(timerRunnable, with.toInstant()); + return timer; + } + + /** + * @return id of the timer + */ + public int searchTimerId() { + int id = 1; + List timerList = this.timerStorage.getAll(); + timerList.sort((timer1, timer2) -> timer1.getId() - timer2.getId()); + for (Timer t : timerList) { + if (t.getId() == id) { + id++; + } else { + return id; + } + } + return id; + } + + /** + * Delete all timers + * + * @return counter counts the existing alarms + */ + public String deleteAllTimers() { + int counter = 0; + for (Timer t : this.timerStorage.getAll()) { + counter++; + this.timerbeep.stopBeep(t); + this.timerStorage.deleteById(t.getPersistentId()); + } + if (counter == 0) { + return "No timers found"; + } + return counter + " timers deleted"; + } + + /** + * Delete one alarm + * + * @param timerNumber + * timerNumber in the storage + * @return alarmNumber + */ + public String deleteTimer(int timerNumber) { + Timer t = getTimer(timerNumber); + this.timerbeep.stopBeep(t); + this.timerStorage.deleteById(t.getPersistentId()); + return this.timerS + t.getId() + " deleted"; + } + + /** + * Go through all timers and delete old ones + * + * @return timer stopped + */ + public String stopRinging() { + List allTimers = getAllTimers(); + for (Timer t : allTimers) { + if (t.isActive() && t.getTimerTime().isBefore(this.environment.getCurrentLocalDateTime())) { + deleteTimer(t.getId()); + } + } + return "Timer stopped"; + } + + /** + * Read out one timer + * + * @param timerNumber + * number of the timer in the storage + * + * @return timer + */ + public Timer getTimer(int timerNumber) { + for (Timer t : this.timerStorage.getAll()) { + if (t.getId() == timerNumber) { + return t; + } + } + throw new NoSuchElementException(); + } + + /** + * Get all timers + * + * @return List of all timers + */ + public List getAllTimers() { + List timerList = this.timerStorage.getAll(); + timerList.sort((timer1, timer2) -> timer1.getId() - timer2.getId()); + return timerList; + } + + /** + * @param timer + * the timer which should be paused + * @return the timer with its new data + */ + public Timer pauseTimer(Timer timer) { + if (timer.isActive()) { + this.timerbeep.stopBeep(timer); + timer.setActive(false); + + LocalDateTime current = LocalDateTime.now(); + LocalDateTime future = timer.getTimerTime(); + timer.duration(Duration.ofMillis(current.until(future, ChronoUnit.MILLIS))); + this.timerStorage.save(timer); + return timer; + } + throw new IllegalArgumentException("Timer is already inactive"); + } + + /** + * @param timer + * time which should be reactivated + * @return the timer with its new data + */ + public Timer reactivateTimer(Timer timer) { + if (!timer.isActive()) { + timer.setActive(true); + LocalDateTime current = LocalDateTime.now(); + int timertime = (int) (timer.duration().toMillis() / 1000); + LocalDateTime newTimerTime = current.plusSeconds(timertime); + timer.setTimerTime(newTimerTime); + this.timerStorage.save(timer); + Runnable timerRunnable = createTimerRunnable(timer); + ZonedDateTime ringtime = this.environment.getCurrentDateTime().with(timer.getTimerTime()); + this.taskScheduler.schedule(timerRunnable, ringtime.toInstant()); + return timer; + } + throw new IllegalArgumentException("Timer is already active"); + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#start() + */ + @Override + public void start() { + List timerList = getAllTimers(); + for (Timer t : timerList) { + Runnable timerRunnable = createTimerRunnable(t); + ZonedDateTime with = this.environment.getCurrentDateTime().with(t.getTimerTime()); + this.taskScheduler.schedule(timerRunnable, with.toInstant()); + } + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.core.service.RunnableService#stop() + */ + @Override + public void stop() { + // Nothing happens here. + } +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerMessageReceiver.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerMessageReceiver.java new file mode 100644 index 000000000..3718ef04d --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerMessageReceiver.java @@ -0,0 +1,66 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceiver; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.Subscription; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SmarthomeFunctionTopics; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SystemTopics; + +/** + * This class controls the alarm sound file, which is used for the alarm clock + * + * @author Patrick Gebhardt, Tim Neumann + */ +@MessageReceiver +public class TimerMessageReceiver { + + @Reference + private TimerBeepService beepService; + + @Reference + private Logger logger; + + /** + * @param message + * message for the system + */ + @Subscription(SystemTopics.SMARTHOME + "/+/+/" + SmarthomeFunctionTopics.MUTE) + public void beepMessage(String message) { + switch (message) { + case "true": + this.beepService.stopBeeping(); + break; + case "false": + // do nothing + break; + default: + this.logger.warn("unkown message {}", message); + break; + } + } +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistry.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistry.java new file mode 100644 index 000000000..53afe551c --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistry.java @@ -0,0 +1,40 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import de.unistuttgart.iaas.amyassist.amy.registry.IRegistry; + +/** + * Interface for the timer registry + * + * @author Patrick Gebhardt + */ +public interface TimerRegistry extends IRegistry { + /** + * @param timer + * the timer which should be saved + */ + @Override + void save(Timer timer); +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistryImpl.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistryImpl.java new file mode 100644 index 000000000..66f8b81c6 --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerRegistryImpl.java @@ -0,0 +1,50 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.registry.AbstractRegistry; + +/** + * The timer registry + * + * @author Patrick Gebhardt + */ +@Service(TimerRegistry.class) +public class TimerRegistryImpl extends AbstractRegistry implements TimerRegistry { + + @Override + protected String getPersistenceUnitName() { + return "TimerRegistry"; + } + + @Nonnull + @Override + public Class getEntityClass() { + return Timer.class; + } + +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResource.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResource.java new file mode 100644 index 000000000..45a94af59 --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResource.java @@ -0,0 +1,169 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import java.net.URI; +import java.util.List; +import java.util.NoSuchElementException; + +import javax.ws.rs.*; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response.Status; +import javax.ws.rs.core.UriInfo; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.utility.rest.Resource; +import de.unistuttgart.iaas.amyassist.amy.utility.rest.ResourceEntity; + +/** + * REST Resource for timer + * + * @author Patrick Gebhardt + */ +@Path(TimerResource.PATH) +public class TimerResource implements Resource { + + /** + * the resource path for this plugin + */ + public static final String PATH = "timer"; + + @Reference + private TimerLogic logic; + + @Context + private UriInfo uri; + + /** + * returns all timers + * + * @return all timers + */ + @GET + @Path("timers") + @Produces(MediaType.APPLICATION_JSON) + public List getAllTimers() { + List timerList = this.logic.getAllTimers(); + for (Timer t : timerList) { + t.setLink(createTimerPath(t.getId())); + } + return this.logic.getAllTimers(); + } + + /** + * returns a specific timer + * + * @param timerNumber + * the requested timer + * @return the specific timer + */ + @GET + @Path("timers/{pathid}") + @Produces(MediaType.APPLICATION_JSON) + public Timer getTimer(@PathParam("pathid") int timerNumber) { + Timer timer; + try { + timer = this.logic.getTimer(timerNumber); + } catch (NoSuchElementException e) { + throw new WebApplicationException("there is no timer" + timerNumber, e, Status.NOT_FOUND); + } + timer.setLink(createTimerPath(timerNumber)); + return timer; + } + + /** + * sets a timer to a given timestamp + * + * @param timer + * the timestamp for the timer + * @return the newly created timer + */ + @POST + @Path("timers/new") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public Timer newTimer(Timer timer) { + timer.setLink(createTimerPath(timer.getId())); + return this.logic.setTimer(timer.getTimerTime()); + } + + /** + * deletes all timers + */ + @POST + @Path("timers/deleteAll") + public void deleteAllTimers() { + this.logic.deleteAllTimers(); + } + + /** + * @param timerNumber + * number of the timer which should be deleted + * @return null + */ + @POST + @Path("timers/delete/{pathid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Timer deleteTimer(@PathParam("pathid") int timerNumber) { + this.logic.deleteTimer(timerNumber); + return null; + } + + /** + * @param timerNumber + * number of the timer which should be activated or deactivated + * @param timerInc + * the incoming timer + * @return null + */ + @POST + @Path("timers/de.activate/{pathid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Timer activatedeactivateAlarm(@PathParam("pathid") int timerNumber, Timer timerInc) { + Timer timer = timerInc; + if (!timer.isActive()) { + this.logic.reactivateTimer(timer); + } else if (timer.isActive()) { + this.logic.pauseTimer(timer); + } + return null; + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.utility.rest.Resource#getPluginDescripion() + */ + @Override + public ResourceEntity getPluginDescripion() { + return null; + } + + private URI createTimerPath(int id) { + return this.uri.getBaseUriBuilder().path(TimerResource.class).path(TimerResource.class, "getTimer") + .build(Integer.valueOf(id)); + } + +} diff --git a/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeech.java b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeech.java new file mode 100644 index 000000000..c636f653c --- /dev/null +++ b/plugins/timer/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeech.java @@ -0,0 +1,241 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; + +/** + * Speech class for timer + * + * @author Patrick Gebhardt + */ +@Service +@SpeechCommand +public class TimerSpeech { + + @Reference + private TimerLogic timerLogic; + + @Reference + private Logger logger; + + private static final String ELEMENTNOTFOUND = "Element not found"; + private static final String NUMBER_MAP_KEY = "number"; + private static String timerS = "Timer "; + private static String secS = " seconds"; + private static String ringS = " will ring in "; + private static String active; + + /** + * Sets a new timer. You can select between hours, minutes and seconds or combinate them + * + * @param entities + * contains the data input + * @return params for the timer + */ + @Intent + public String setTimer(Map entities) { + int hours = 0; + int minutes = 0; + int seconds = 0; + if (entities.get("hour") != null) { + hours = entities.get("hour").getNumber(); + } + if (entities.get("minute") != null) { + minutes = entities.get("minute").getNumber(); + } + if (entities.get("second") != null) { + seconds = entities.get("second").getNumber(); + } + if (hours == 0 && minutes == 0 && seconds == 0) { + return "No value is set"; + } + LocalDateTime timerDate = LocalDateTime.now().plusHours(hours).plusMinutes(minutes).plusSeconds(seconds) + .truncatedTo(ChronoUnit.SECONDS); + Timer timer = this.timerLogic.setTimer(timerDate); + return timerS + timer.getId() + " set"; + } + + /** + * @param entities + * contains the data input + * @return the number of the paused Timer + */ + @Intent + public String pauseTimer(Map entities) { + Timer timer = this.timerLogic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber()); + this.timerLogic.pauseTimer(timer); + return timerS + timer.getId() + " paused"; + } + + /** + * @param entities + * contains the data input + * @return the number of the resumed Timer + */ + @Intent + public String reactivateTimer(Map entities) { + Timer timer = this.timerLogic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber()); + this.timerLogic.reactivateTimer(timer); + return timerS + timer.getId() + " resumed"; + } + + /** + * Resets all alarms or timers + * + * @param entities + * contains the data input + * @return resetAlarms or resetTimers + */ + @Intent + public String resetTimerObjects(Map entities) { + return this.timerLogic.deleteAllTimers(); + } + + /** + * Deletes one specific alarm or timer + * + * @param entities + * contains the data input + * @return deleteAlarm or deleteTimer + */ + @Intent + public String deleteTimerObject(Map entities) { + try { + return this.timerLogic.deleteTimer(entities.get(NUMBER_MAP_KEY).getNumber()); + } catch (NoSuchElementException e) { + this.logException(e); + return ELEMENTNOTFOUND; + } + } + + /** + * Deactivates currently ringing timer + * + * @param entities + * contains the data input + * @return timer stopped + */ + @Intent + public String stopRinging(Map entities) { + return this.timerLogic.stopRinging(); + } + + /** + * gets one specific alarm or timer + * + * @param entities + * contains the data input + * @return getAlarm or getTimer + */ + @Intent + public String getTimerObject(Map entities) { + try { + Timer timer = this.timerLogic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber()); + return outputTimer(timer); + } catch (NoSuchElementException e) { + this.logException(e); + return ELEMENTNOTFOUND; + } + } + + /** + * gets all alarms or timers + * + * @param entities + * contains the data input + * @return getAllAlarms or getAllTimers + */ + @Intent + public String getAllTimerObjects(Map entities) { + List timers = this.timerLogic.getAllTimers(); + if (timers.isEmpty()) { + return "No timers found"; + } + String[] stringTimers = new String[timers.size()]; + for (int i = 0; i < timers.size(); i++) { + stringTimers[i] = outputTimer(timers.get(i)); + } + return String.join("\n", stringTimers); + } + + /** + * Gets remaining timer delay + * + * @param entities + * contains the data input + * @return remaining timer delay + */ + @Intent + public String getRemainingTimerDelay(Map entities) { + try { + return outputTimer(this.timerLogic.getTimer(entities.get(NUMBER_MAP_KEY).getNumber())); + } catch (NoSuchElementException e) { + this.logException(e); + return ELEMENTNOTFOUND; + } + } + + private void logException(Exception e) { + this.logger.error("Exception Thrown!", e); + } + + private static String outputTimer(Timer timer) { + Duration timeDiff = timer.getRemainingTime(); + int sec = (int) ((timeDiff.toMillis() / 1000) % 60); + int min = (int) (timeDiff.toMinutes() % 60); + int hours = (int) (timeDiff.toHours()); + int timerNumber = timer.getId(); + if (!timer.isActive()) { + active = " but is paused"; + } else { + active = ""; + } + + if (timeDiff.isZero() || timeDiff.isNegative()) { + return timerS + timerNumber + " is ringing right now."; + } + if (hours == 0) { + if (min == 0) { + return timerS + timerNumber + ringS + sec + secS + active; + } + return timerS + timerNumber + ringS + min + " minutes and " + sec + secS + active; + } + return timerS + timerNumber + ringS + hours + " hours and " + min + " minutes and " + sec + secS + active; + } + +} diff --git a/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-timer.natlangMeta b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-timer.natlangMeta new file mode 100644 index 000000000..c5adc2ce1 --- /dev/null +++ b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-timer.natlangMeta @@ -0,0 +1,2 @@ +.aims: +TimerSpeech.aim.xml \ No newline at end of file diff --git a/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..0d27462d0 --- /dev/null +++ b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1,3 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.timer.TimerBeepService +de.unistuttgart.iaas.amyassist.amy.plugin.timer.TimerLogic +de.unistuttgart.iaas.amyassist.amy.plugin.timer.TimerRegistryImpl \ No newline at end of file diff --git a/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers new file mode 100644 index 000000000..bbfdb012e --- /dev/null +++ b/plugins/timer/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.timer.TimerMessageReceiver diff --git a/plugins/timer/src/main/resources/META-INF/javax.ws.rs.Path b/plugins/timer/src/main/resources/META-INF/javax.ws.rs.Path new file mode 100644 index 000000000..a65e3d30a --- /dev/null +++ b/plugins/timer/src/main/resources/META-INF/javax.ws.rs.Path @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.timer.TimerResource diff --git a/plugins/timer/src/main/resources/TimerSpeech.aim.xml b/plugins/timer/src/main/resources/TimerSpeech.aim.xml new file mode 100644 index 000000000..209ce4ae3 --- /dev/null +++ b/plugins/timer/src/main/resources/TimerSpeech.aim.xml @@ -0,0 +1,76 @@ + + + + [set|create] [a] timer (for|on|at) [{hour} hour] [{minute} minute] [{second} second] + + + {amyinteger} + + + {amyinteger} + + + {amyinteger} + + + + + pause timer {number} + + + {amyinteger} + + + + + (resume|reactivate) timer {number} + + + {amyinteger} + + + + + delete all timers + + + (delete|deactivate) timer {number} + + + {amyinteger} + + + + + stop timer + + + get timer {number} + + + {amyinteger} + + + + + get all timers + + + when (does|is) timer {number} (ringing|ring) + + + {amyinteger} + + + + + \ No newline at end of file diff --git a/plugins/timer/src/main/resources/de/unistuttgart/iaas/amyassist/amy/plugin/timer/alarmsound.wav b/plugins/timer/src/main/resources/de/unistuttgart/iaas/amyassist/amy/plugin/timer/alarmsound.wav new file mode 100644 index 000000000..200d1db9c Binary files /dev/null and b/plugins/timer/src/main/resources/de/unistuttgart/iaas/amyassist/amy/plugin/timer/alarmsound.wav differ diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepServiceTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepServiceTest.java new file mode 100644 index 000000000..a340f7d0d --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerBeepServiceTest.java @@ -0,0 +1,90 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * @author Patrick Gebhardt + */ +@ExtendWith(FrameworkExtension.class) +public class TimerBeepServiceTest { + + @Reference + private TestFramework framework; + + private TimerBeepService tbs; + + private Set timers = new HashSet<>(); + + /** + * Initializes the class variables before each test + */ + @BeforeEach + public void setup() { + this.tbs = this.framework.mockService(TimerBeepService.class); + } + + /** + * Tests beep(timer) + */ + @Test + public void beepTimerTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 8, 15, 11, 11); + Timer timer = new Timer(1, timerTime, null, true); + when(this.tbs.beep(timer)).thenReturn(this.timers); + this.timers.add(timer.getId()); + this.tbs.beep(timer); + verify(this.tbs).beep(timer); + assertThat(this.tbs.beep(timer).size(), is(1)); + } + + /** + * Tests stopBeep(timer) + */ + @Test + public void stopBeepAlarmTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 8, 15, 11, 11); + Timer timer = new Timer(1, timerTime, null, true); + when(this.tbs.stopBeep(timer)).thenReturn(this.timers); + this.tbs.stopBeep(timer); + verify(this.tbs).stopBeep(timer); + assertThat(this.tbs.stopBeep(timer).size(), is(0)); + } + +} diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogicTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogicTest.java new file mode 100644 index 000000000..9d08900e5 --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerLogicTest.java @@ -0,0 +1,298 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.io.Environment; +import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.messagehub.MessageHub; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * Test class for the TimerLogic class + * + * @authorPatrick Gebhardt + */ +@ExtendWith(FrameworkExtension.class) +public class TimerLogicTest { + + @Reference + private TestFramework framework; + + private TimerLogic tlogic; + + private TimerBeepService tbs; + + private TaskScheduler scheduler; + + private TimerRegistry timerStorage; + + private Environment env; + + private MessageHub messageHub; + + private List timers = new ArrayList<>(); + + private int timerNumber; + + /** + * Initializes the class variables before each test + */ + @BeforeEach + public void setup() { + this.env = this.framework.mockService(Environment.class); + this.scheduler = this.framework.mockService(TaskScheduler.class); + this.setTbs(this.framework.mockService(TimerBeepService.class)); + this.timerStorage = this.framework.mockService(TimerRegistry.class); + this.messageHub = this.framework.mockService(MessageHub.class); + this.tlogic = this.framework.setServiceUnderTest(TimerLogic.class); + + when(this.timerStorage.getAll()).thenReturn(this.timers); + } + + /** + * Tests set Timer with normal arguments + */ + @Test + public void testSetTimer() { + when(this.env.getCurrentDateTime()).thenReturn(ZonedDateTime.of(2018, 2, 1, 4, 21, 55, 987, ZoneId.of("Z"))); + LocalDateTime timerTime = LocalDateTime.of(2018, 2, 1, 4, 21, 55); + Timer t = new Timer(1, timerTime, null, true); + this.tlogic.setTimer(timerTime); + verify(this.scheduler).schedule(ArgumentMatchers.any(Runnable.class), ArgumentMatchers.any(Instant.class)); + assertThat(this.tlogic.setTimer(timerTime).toString(), is(t.toString())); + } + + /** + * Tests resetTimers + */ + @Test + public void testResetTimers() { + List returnedTimers = createTimers(3, true); + this.timerNumber = 2; + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.deleteAllTimers(), is("3 timers deleted")); + } + + /** + * Tests resetTimers with no timers + */ + @Test + public void testResetTimersNoTimers() { + assertThat(this.tlogic.deleteAllTimers(), is("No timers found")); + } + + /** + * Tests deleteTimers + */ + @Test + protected void testDeleteTimer() { + List returnedTimers = createTimers(3, true); + this.timerNumber = 2; + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.deleteTimer(2), is("Timer " + this.timerNumber + " deleted")); + } + + /** + * Tests deleteTimer with non existent timer + */ + @Test + protected void testDeleteTimerNotFound() { + assertThrows(NoSuchElementException.class, () -> this.tlogic.deleteTimer(4)); + } + + /** + * Tests getTimer with valid argument + */ + @Test + protected void testGetTimer() { + when(this.timerStorage.getAll()).thenReturn(createTimers(3, true)); + this.timerNumber = 2; + Timer returnedTimer = this.tlogic.getTimer(this.timerNumber); + assertThat(returnedTimer.getId(), is(2)); + } + + /** + * Tests getTimer with non existent timer + */ + @Test + protected void testGetTimerNotFound() { + when(this.timerStorage.getAll()).thenReturn(createTimers(3, true)); + this.timerNumber = 4; + assertThrows(NoSuchElementException.class, () -> this.tlogic.getTimer(this.timerNumber)); + } + + /** + * Tests getAllTimers with some timers + */ + @Test + protected void testGetAllTimers() { + createTimers(3, true); + List returnedTimers = this.tlogic.getAllTimers(); + assertThat(returnedTimers, hasSize(3)); + } + + /** + * Tests getAllTimers with no timers + */ + @Test + protected void testGetAllTimersNoTimers() { + List returnedTimers = this.tlogic.getAllTimers(); + assertThat(returnedTimers, hasSize(0)); + } + + /** + * Tests reactivateTimer with valid arguments + */ + @Test + protected void testReactivateTimer() { + List returnedTimers = createTimers(3, true); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + when(this.env.getCurrentDateTime()).thenReturn(ZonedDateTime.of(2018, 2, 1, 4, 21, 55, 987, ZoneId.of("Z"))); + Timer t2 = new Timer(3, LocalDateTime.of(2018, 9, 5, 12, 20, 0), null, true); + this.tlogic.pauseTimer(returnedTimers.get(2)); + assertThat(this.tlogic.reactivateTimer(returnedTimers.get(2)).toString(), is(t2.toString())); + } + + /** + * Tests pauseTimer with valid arguments + */ + @Test + protected void testPauseTimer() { + List returnedTimers = createTimers(3, true); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + when(this.env.getCurrentDateTime()).thenReturn(ZonedDateTime.of(2018, 2, 1, 4, 21, 55, 987, ZoneId.of("Z"))); + Timer t2 = new Timer(3, LocalDateTime.of(2018, 9, 5, 12, 20, 0), null, false); + assertThat(this.tlogic.pauseTimer(returnedTimers.get(2)).toString(), is(t2.toString())); + } + + /** + * Tests reactivateTimer with notvalid arguments + */ + @Test + protected void testReactivateTimerNotValid() { + List returnedTimers = createTimers(3, true); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + when(this.env.getCurrentDateTime()).thenReturn(ZonedDateTime.of(2018, 2, 1, 4, 21, 55, 987, ZoneId.of("Z"))); + assertThrows(IllegalArgumentException.class, () -> this.tlogic.reactivateTimer(returnedTimers.get(2))); + } + + /** + * Tests pauseTimer with notvalid arguments + */ + @Test + protected void testPauseTimerNotValid() { + List returnedTimers = createTimers(3, false); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + when(this.env.getCurrentDateTime()).thenReturn(ZonedDateTime.of(2018, 2, 1, 4, 21, 55, 987, ZoneId.of("Z"))); + assertThrows(IllegalArgumentException.class, () -> this.tlogic.pauseTimer(returnedTimers.get(2))); + } + + /** + * Tests SearchTimerId method + */ + @Test + protected void testSearchTimerIdWithList() { + List returnedTimers = createTimers(3, false); + returnedTimers.remove(1); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.searchTimerId(), is(2)); + } + + /** + * Tests SearchTimerId method + */ + @Test + protected void testSearchTimerIdWithoutList() { + List returnedTimers = new ArrayList<>(); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.searchTimerId(), is(1)); + } + + /** + * Tests the deleteAllTimers method + */ + @Test + protected void testDeleteAllTimers() { + List returnedTimers = createTimers(3, false); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.deleteAllTimers(), is("3 timers deleted")); + } + + /** + * Tests the deleteAllTimers method + */ + @Test + protected void testDeleteAllTimersNoTimers() { + List returnedTimers = createTimers(0, false); + when(this.timerStorage.getAll()).thenReturn(returnedTimers); + assertThat(this.tlogic.deleteAllTimers(), is("No timers found")); + } + + private List createTimers(int amount, boolean active) { + for (int i = 1; i <= amount; i++) { + Timer mockTimer = new Timer(i, LocalDateTime.of(2018, 9, 5, 12, 20, 0), null, active); + this.timers.add(mockTimer); + } + return this.timers; + } + + /** + * Get's {@link #tbs abs} + * + * @return abs + */ + public TimerBeepService getTbs() { + return this.tbs; + } + + /** + * Set's {@link #tbs abs} + * + * @param tbs + * timerBeepService + */ + public void setTbs(TimerBeepService tbs) { + this.tbs = tbs; + } +} diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResourceTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResourceTest.java new file mode 100644 index 000000000..ceab9882b --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerResourceTest.java @@ -0,0 +1,198 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; + +import javax.ws.rs.client.Entity; +import javax.ws.rs.client.WebTarget; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * test for the rest resource of the timer + * + * @author Patrick Gebhardt, Muhammed Kaya + */ +@ExtendWith(FrameworkExtension.class) +class TimerResourceTest { + + @Reference + private TestFramework framework; + + private TimerLogic tLogic; + + private WebTarget target; + + private TimerRegistry timerStorage; + + private List timers = new ArrayList<>(); + + private Response r; + + /** + * setup server and client for requests and responses + */ + @BeforeEach + public void setUp() { + this.tLogic = this.framework.mockService(TimerLogic.class); + this.target = this.framework.setRESTResource(TimerResource.class); + this.timerStorage = this.framework.mockService(TimerRegistry.class); + + when(this.timerStorage.getAll()).thenReturn(this.timers); + } + + /** + * Test method to get all timers + */ + @Test + void testGetAllTimers() { + List returnedTimers = createTimers(3, true); + when(this.tLogic.getAllTimers()).thenReturn(returnedTimers); + this.r = this.target.path("timers").request().get(); + assertThat(returnedTimers.size(), is(3)); + assertEquals(200, this.r.getStatus()); + + } + + private List createTimers(int amount, boolean active) { + for (int i = 1; i <= amount; i++) { + Timer mockTimer = new Timer(i, LocalDateTime.of(2018, 9, 21, 21, 21), null, active); + this.timers.add(mockTimer); + } + return this.timers; + } + + /** + * Test method to get one timer + * + */ + @Test + void testGetTimer() { + List returnedTimers = createTimers(3, true); + when(this.tLogic.getTimer(2)).thenReturn(returnedTimers.get(1)); + when(this.tLogic.getTimer(5)).thenThrow(new NoSuchElementException()); + + this.r = this.target.path("timers/0").request().get(); + assertEquals(500, this.r.getStatus()); + + this.r = this.target.path("timers/2").request().get(); + assertEquals(200, this.r.getStatus()); + assertEquals(this.tLogic.getTimer(2).getId(), 2); + + this.r = this.target.path("timers/5").request().get(); + assertEquals(404, this.r.getStatus()); + assertTrue(this.r.readEntity(String.class).startsWith("there is no timer5")); + } + + /** + * Test method to delete all Timers + */ + @Test + void testDeleteAllTimers() { + this.r = this.target.path("timers/deleteAll").request().post(null); + assertEquals(204, this.r.getStatus()); + verify(this.tLogic).deleteAllTimers(); + } + + /** + * Test method for new Timers + */ + @Test + void testNewTimer() { + LocalDateTime newTimerTime = LocalDateTime.of(2018, 9, 21, 21, 21); + Timer newTimer = new Timer(1, newTimerTime, null, true); + when(this.tLogic.setTimer(newTimerTime)).thenReturn(newTimer); + Entity entity = Entity.entity(newTimer, MediaType.APPLICATION_JSON); + + this.r = this.target.path("timers/new").request().post(entity); + + Timer timerRead = this.r.readEntity(Timer.class); + assertEquals(200, this.r.getStatus()); + assertEquals(newTimer.getId(), timerRead.getId()); + assertEquals(newTimer.getTimerTime(), timerRead.getTimerTime()); + // time.now() is not testable + assertEquals(newTimer.isActive(), timerRead.isActive()); + } + + /** + * Test method to reactivate a paused timer + */ + @Test + void testReactivateTimer() { + List returnedTimers = createTimers(3, false); + when(this.tLogic.getTimer(1)).thenReturn(returnedTimers.get(0)); + Entity entity = Entity.entity(returnedTimers.get(0), MediaType.APPLICATION_JSON); + + this.r = this.target.path("timers/de.activate/1").request().post(entity); + assertEquals(204, this.r.getStatus()); + verify(this.tLogic).reactivateTimer(any()); + } + + /** + * Test method to pause a running timer + */ + @Test + void testPauseTimer() { + List returnedTimers = createTimers(3, true); + when(this.tLogic.getTimer(1)).thenReturn(returnedTimers.get(0)); + Entity entity = Entity.entity(returnedTimers.get(0), MediaType.APPLICATION_JSON); + + this.r = this.target.path("timers/de.activate/1").request().post(entity); + assertEquals(204, this.r.getStatus()); + verify(this.tLogic).pauseTimer(any()); + } + + /** + * Test method to delete a timer + */ + @Test + void testDeleteTimer() { + List returnedTimers = createTimers(3, true); + when(this.tLogic.getTimer(1)).thenReturn(returnedTimers.get(0)); + Entity entity = Entity.entity(returnedTimers.get(0), MediaType.APPLICATION_JSON); + + this.r = this.target.path("timers/delete/1").request().post(entity); + assertEquals(204, this.r.getStatus()); + verify(this.tLogic).deleteTimer(1); + } + +} diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeechTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeechTest.java new file mode 100644 index 000000000..ebe250eff --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerSpeechTest.java @@ -0,0 +1,228 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.HashMap; +import java.util.Map; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * Test class for the timer speech class + * + * @author Patrick Gebhardt + */ +@ExtendWith({ MockitoExtension.class, FrameworkExtension.class }) +public class TimerSpeechTest { + + @Reference + private TestFramework framework; + + private TimerSpeech tSpeech; + + private TimerLogic tLogic; + + @Mock + private EntityData hour; + + @Mock + private EntityData minute; + + @Mock + private EntityData second; + + @Mock + private EntityData number; + + /** + * Initializes the class variables before each test + */ + @BeforeEach + public void init() { + this.tLogic = this.framework.mockService(TimerLogic.class); + this.framework.mockService(TimerRegistry.class); + this.tSpeech = this.framework.setServiceUnderTest(TimerSpeech.class); + } + + /** + * Tests the setTimer method with valid arguments + */ + @Test + public void setTimerTest() { + Map map = new HashMap<>(); + when(this.hour.getNumber()).thenReturn(1); + when(this.minute.getNumber()).thenReturn(5); + when(this.second.getNumber()).thenReturn(30); + map.put("hour", this.hour); + map.put("minute", this.minute); + map.put("second", this.second); + LocalDateTime timerDate = LocalDateTime.now().plusHours(this.hour.getNumber()) + .plusMinutes(this.minute.getNumber()).plusSeconds(this.second.getNumber()) + .truncatedTo(ChronoUnit.SECONDS); + Timer t = new Timer(1, timerDate, null, true); + when(this.tLogic.setTimer(timerDate)).thenReturn(t); + assertThat(this.tSpeech.setTimer(map), is("Timer 1 set")); + } + + /** + * Tests the setTimer method with invalid arguments + */ + @Test + public void setTimerInvalidTest() { + Map map = new HashMap<>(); + when(this.hour.getNumber()).thenReturn(0); + when(this.minute.getNumber()).thenReturn(0); + when(this.second.getNumber()).thenReturn(0); + map.put("hour", this.hour); + map.put("minute", this.minute); + map.put("second", this.second); + assertThat(this.tSpeech.setTimer(map), is("No value is set")); + } + + /** + * Tests the pauseTimer method + */ + @Test + public void pauseTimerTest() { + Map map = new HashMap<>(); + map.put("number", this.number); + when(map.get("number").getNumber()).thenReturn(1); + LocalDateTime timerDate = LocalDateTime.now().plusHours(this.hour.getNumber()) + .plusMinutes(this.minute.getNumber()).plusSeconds(this.second.getNumber()) + .truncatedTo(ChronoUnit.SECONDS); + Timer t = new Timer(1, timerDate, null, true); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.pauseTimer(map), is("Timer 1 paused")); + } + + /** + * Tests the reactivateTimer method + */ + @Test + public void reactivateTimerTest() { + Map map = new HashMap<>(); + map.put("number", this.number); + when(map.get("number").getNumber()).thenReturn(1); + LocalDateTime timerDate = LocalDateTime.now().plusHours(this.hour.getNumber()) + .plusMinutes(this.minute.getNumber()).plusSeconds(this.second.getNumber()) + .truncatedTo(ChronoUnit.SECONDS); + Timer t = new Timer(1, timerDate, null, false); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.reactivateTimer(map), is("Timer 1 resumed")); + } + + /** + * Tests the resetTimerObjects method + */ + @Test + public void resetTimerObjectsTest() { + Map map = new HashMap<>(); + assertThat(this.tSpeech.resetTimerObjects(map), is(this.tLogic.deleteAllTimers())); + } + + /** + * Tests the deleteTimerObject method + */ + @Test + public void deleteTimerObjectTest() { + Map map = new HashMap<>(); + when(this.number.getNumber()).thenReturn(1); + map.put("number", this.number); + assertThat(this.tSpeech.deleteTimerObject(map), is(this.tLogic.deleteTimer(1))); + } + + /** + * Tests the outputTimer method and the getTimerObjectMethod + */ + @Test + public void outputTimerNowTest() { + Map map = new HashMap<>(); + when(this.number.getNumber()).thenReturn(1); + map.put("number", this.number); + LocalDateTime timerDate = LocalDateTime.of(2018, 9, 6, 12, 11, 10); + Timer t = new Timer(1, timerDate, Duration.ZERO, false); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.getTimerObject(map), is("Timer 1 is ringing right now.")); + } + + /** + * Tests the outputTimer method and the getTimerObjectMethod + */ + @Test + public void outputTimerSecondTest() { + Map map = new HashMap<>(); + when(this.number.getNumber()).thenReturn(1); + map.put("number", this.number); + LocalDateTime timerDate = LocalDateTime.of(2018, 9, 6, 12, 11, 10); + Timer t = new Timer(1, timerDate, Duration.ofSeconds(10), false); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.getTimerObject(map), is("Timer 1 will ring in 10 seconds but is paused")); + } + + /** + * Tests the outputTimer method and the getTimerObjectMethod + */ + @Test + public void outputTimerMinuteTest() { + Map map = new HashMap<>(); + when(this.number.getNumber()).thenReturn(1); + map.put("number", this.number); + LocalDateTime timerDate = LocalDateTime.of(2018, 9, 6, 12, 11, 10); + Timer t = new Timer(1, timerDate, Duration.ofMinutes(10), false); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.getTimerObject(map), is("Timer 1 will ring in 10 minutes and 0 seconds but is paused")); + } + + /** + * Tests the outputTimer method and the getTimerObjectMethod + */ + @Test + public void outputTimerHourTest() { + Map map = new HashMap<>(); + when(this.number.getNumber()).thenReturn(1); + map.put("number", this.number); + LocalDateTime timerDate = LocalDateTime.of(2018, 9, 6, 12, 11, 10); + Timer t = new Timer(1, timerDate, Duration.ofHours(10), false); + when(this.tLogic.getTimer(1)).thenReturn(t); + assertThat(this.tSpeech.getTimerObject(map), + is("Timer 1 will ring in 10 hours and 0 minutes and 0 seconds but is paused")); + } + +} diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerStorageTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerStorageTest.java new file mode 100644 index 000000000..19941b7b1 --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerStorageTest.java @@ -0,0 +1,112 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.mockito.Mockito.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * Test class for the timer storage class + * + * @author Patrick Gebhardt + * + */ +@ExtendWith(FrameworkExtension.class) +public class TimerStorageTest { + + private TimerRegistry timerStorage; + + private TimerRegistryImpl timerRegImpl; + + @Reference + private TestFramework framework; + + /** + * Initializes the class variables before every test + */ + @BeforeEach + void setup() { + this.timerStorage = this.framework.mockService(TimerRegistry.class); + this.timerRegImpl = this.framework.mockService(TimerRegistryImpl.class); + reset(this.timerStorage); + } + + /** + * Tests the store timer method + */ + @Test + void testStoreTimer() { + LocalDateTime timerTime = LocalDateTime.of(2018, 8, 15, 11, 11); + Timer timer = new Timer(1, timerTime, null, true); + + this.timerStorage.save(timer); + verify(this.timerStorage).save(timer); + } + + /** + * Tests deleteTimer with normal case + */ + @Test + public void testDeleteTimer() { + LocalDateTime timerTime = LocalDateTime.of(2018, 8, 15, 11, 11); + Timer timer = new Timer(1, timerTime, null, true); + this.timerStorage.save(timer); + this.timerStorage.deleteById(timer.getPersistentId()); + verify(this.timerStorage).deleteById(timer.getPersistentId()); + assertThat(true, is(this.timerStorage.getAll().isEmpty())); + } + + /** + * Tests deleteTimer with non existent timer + */ + @Test + public void testDeleteTimerNotFound() { + LocalDateTime timerTime = LocalDateTime.of(2018, 8, 15, 11, 11); + Timer timer = new Timer(1, timerTime, null, true); + this.timerStorage.save(timer); + this.timerStorage.deleteById(timer.getPersistentId() + 1); + assertThat(true, is(this.timerStorage.getAll().isEmpty())); + } + + /** + * tests the getPersistenceUnitName method + */ + @Test + public void getPersistenceUnitNameTest() { + when(this.timerRegImpl.getPersistenceUnitName()).thenReturn("TimerRegistry"); + assertThat(this.timerRegImpl.getPersistenceUnitName(), is("TimerRegistry")); + } + +} diff --git a/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerTest.java b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerTest.java new file mode 100644 index 000000000..d285e5504 --- /dev/null +++ b/plugins/timer/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/timer/TimerTest.java @@ -0,0 +1,120 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.timer; + +import static org.hamcrest.MatcherAssert.*; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.time.Duration; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; + +/** + * Class to test the Timer class + * + * @author Patrick Gebhardt + */ +@ExtendWith(FrameworkExtension.class) +public class TimerTest { + + /** + * Test the timer + */ + @Test + public void timerTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + Timer t1 = new Timer(1, timerTime, null, true); + assertEquals(1, t1.getId()); + + LocalDateTime testTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + assertThat(t1.getTimerTime(), is(equalTo(testTime))); + } + + /** + * Test the timer with an invalid id + */ + @Test + public void timerInvalidIdTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + assertThrows(IllegalArgumentException.class, () -> new Timer(0, timerTime, null, true)); + } + + /** + * Tests the toString method + */ + @Test + public void toStringTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + Timer t1 = new Timer(1, timerTime, null, true); + assertThat(t1.toString(), is("1:12:11:10")); + } + + /** + * tests the getRemainingTime method + */ + @Test + public void getRemainingTimeTest() { + LocalDateTime timerTime = LocalDateTime.now().plusMinutes(1); + Timer t1 = new Timer(1, timerTime, null, true); + assertTrue(t1.getRemainingTime().get(ChronoUnit.SECONDS) <= Duration.ofMillis(60000).getSeconds() + && t1.getRemainingTime().get(ChronoUnit.SECONDS) >= Duration.ofMillis(59000).getSeconds()); + } + + /** + * tests the getter for the ID + */ + @Test + public void getIdTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + Timer t1 = new Timer(1, timerTime, null, true); + assertThat(t1.getId(), is(1)); + } + + /** + * tests the getter for the time of the timer + */ + @Test + public void getTimerTimeTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + Timer t1 = new Timer(1, timerTime, null, true); + assertThat(t1.getTimerTime(), is(timerTime)); + } + + /** + * tests the getter whether the timer is active or not + */ + @Test + public void isActiveTest() { + LocalDateTime timerTime = LocalDateTime.of(2018, 10, 11, 12, 11, 10); + Timer t1 = new Timer(1, timerTime, null, true); + assertThat(t1.isActive(), is(true)); + } + +} diff --git a/plugins/tosca/.settings/org.eclipse.jdt.core.prefs b/plugins/tosca/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/plugins/tosca/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/plugins/tosca/.settings/org.eclipse.jdt.ui.prefs b/plugins/tosca/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/plugins/tosca/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/plugins/tosca/.settings/org.eclipse.m2e.core.prefs b/plugins/tosca/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/tosca/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/tosca/pom.xml b/plugins/tosca/pom.xml new file mode 100644 index 000000000..18f1185c4 --- /dev/null +++ b/plugins/tosca/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + amy-plugin-tosca + Amy plugin tosca + The Amy tosca plugin + + de.unistuttgart.iaas.amyassist + amy-plugin + 0.6.0 + ../pom.xml + + + + jitpack.io + https://jitpack.io + + + + + com.github.OpenTOSCA + container-client + master-SNAPSHOT + + + + com.googlecode.clichemaven + cliche + ${cliche.version} + + + diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaConsole.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaConsole.java new file mode 100644 index 000000000..d0eadbd0f --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaConsole.java @@ -0,0 +1,95 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca; + +import java.util.List; +import java.util.NoSuchElementException; + +import org.opentosca.containerapi.client.model.Application; + +import asg.cliche.Command; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; + +/** + * The console class for the tosca plugin + * + * @author Leon Kiefer, Tim Neumann + */ +public class ToscaConsole { + + @Reference + private ToscaLogic logic; + + /** + * List all apps + * + * @return A string to output + */ + @Command + public String listApps() { + List apps = this.logic.getInstalledApps(); + + StringBuilder ret = new StringBuilder("Found "); + + ret.append(apps.size()); + ret.append(" application"); + if (apps.size() != 1) { + ret.append('s'); + } + ret.append(": \n"); + + apps.forEach(a -> { + ret.append(" "); + ret.append(a.getDisplayName()); + ret.append(" : "); + ret.append(a.getDescription()); + ret.append("\n "); + }); + ret.setLength(ret.length() - 2); + + return ret.toString(); + } + + /** + * Install a app + * + * @param app + * The app name + * @param config + * The config name + * + * @return A string to output. + */ + @Command + public String install(String app, String config) { + try { + this.logic.install(app, config); + } catch (NoSuchElementException | IllegalArgumentException e) { + return e.getMessage(); + } + + return "Installing."; + + } +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLibraryAdapter.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLibraryAdapter.java new file mode 100644 index 000000000..bb8ea1030 --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLibraryAdapter.java @@ -0,0 +1,52 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca; + +import org.opentosca.containerapi.client.IOpenTOSCAContainerAPIClient; +import org.opentosca.containerapi.client.impl.OpenTOSCAContainerLegacyAPIClient; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; + +/** + * Adapter to the tosca client library + * + * @author Tim Neumann + */ +@Service +public class ToscaLibraryAdapter { + + /** + * Initializes the tosca library and returns its client. + * + * @param containerHost + * The container host to use. + * @param containerHostInternal + * The internal container host to use. + * @return The created client + * @see OpenTOSCAContainerLegacyAPIClient#OpenTOSCAContainerLegacyAPIClient(String, String) + */ + public IOpenTOSCAContainerAPIClient createLibrary(String containerHost, String containerHostInternal) { + return new OpenTOSCAContainerLegacyAPIClient(containerHost, containerHostInternal); + } +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogic.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogic.java new file mode 100644 index 000000000..093ba5226 --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogic.java @@ -0,0 +1,195 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca; + +import java.util.*; + +import org.opentosca.containerapi.client.IOpenTOSCAContainerAPIClient; +import org.opentosca.containerapi.client.model.Application; +import org.opentosca.containerapi.client.model.ServiceInstance; + +import de.unistuttgart.iaas.amyassist.amy.core.configuration.WithDefault; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; +import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationEntry; +import de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationRegistry; + +/** + * The tosca logic class + * + * @author Felix Burk, Leon Kiefer, Tim Neumann + */ +@Service +public class ToscaLogic { + + private static final String STORAGE_KEY = "installing"; + + @WithDefault + @Reference + private Properties configuration; + + @Reference + private TaskScheduler scheduler; + + @Reference + private ConfigurationRegistry registry; + + @Reference + private ToscaLibraryAdapter adapter; + + @Reference + private IStorage storage; + + /** + * internal tosca client + */ + private IOpenTOSCAContainerAPIClient apiClient; + + @PostConstruct + private void connect() { + String containerHost = this.configuration.getProperty("CONTAINER_HOST"); + String containerHostInternal = this.configuration.getProperty("CONTAINER_HOST_INTERNAL"); + this.apiClient = this.adapter.createLibrary(containerHost, containerHostInternal); + } + + /** + * @return All registered configuration entrys. + */ + public List getConfigurations() { + return this.registry.getAll(); + } + + /** + * Installs the application with the given name, using the parameters in the configuration with the given name + * + * @param appName + * The name of the application to install + * @param configurationName + * The name of the configuration to use + * @throws IllegalArgumentException + * When the configuration does not contain all required keys. + * @throws NoSuchElementException + * When no app with the given name is installed. + */ + public void install(String appName, String configurationName) { + List apps = getInstalledApps(); + for (Application app : apps) { + if (app.getDisplayName().equalsIgnoreCase(appName)) { + install(app, configurationName); + return; + } + } + throw new NoSuchElementException("No app with this name is installed."); + } + + /** + * Installs the given application with the parameters in the configuration with the given name + * + * @param app + * The application to install + * @param conigurationName + * The name of the configuration to use + * @throws IllegalArgumentException + * When the configuration does not contain all required keys. + */ + public void install(Application app, String conigurationName) { + List keys = app.getInputParameters(); + Map configurations = new HashMap<>(); + getConfigurations().stream().filter(c -> c.getTag().equals(conigurationName)) + .forEach(c -> configurations.put(c.getKey().toLowerCase(), c.getValue())); + Map parameters = new HashMap<>(); + for (String key : keys) { + if (!configurations.containsKey(key.toLowerCase())) + throw new IllegalArgumentException( + "The configuration with the given name does not have all required keys."); + parameters.put(key, configurations.get(key.toLowerCase())); + } + install(app, parameters); + } + + /** + * Installs the given application with the given parameters + * + * @param app + * The application to install + * @param parameters + * The parameters to use + */ + public void install(Application app, Map parameters) { + this.scheduler.execute(() -> installWait(app, parameters)); + } + + private void installWait(Application app, Map parameters) { + this.storage.put(STORAGE_KEY, app.getId()); + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + this.apiClient.createServiceInstance(app, parameters); + } + + /** + * poll the tosca api and wait for all instances of the application to be not "CREATING" + */ + public void waitForInstall() { + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + boolean wait = true; + while (!Thread.currentThread().isInterrupted() && wait) { + if (this.storage.has(STORAGE_KEY)) { + String id = this.storage.get(STORAGE_KEY); + Application application = this.apiClient.getApplication(id); + wait = this.isCreating(application); + } else { + wait = false; + } + if (wait) { + try { + Thread.sleep(5); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + + private boolean isCreating(Application application) { + for (ServiceInstance serviceInstance : this.apiClient.getServiceInstances(application)) { + if ("CREATING".equalsIgnoreCase(serviceInstance.getState())) { + return true; + } + } + return false; + } + + /** + * Get a list of installed apps. + * + * @return A list of installed apps. + */ + public List getInstalledApps() { + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + return this.apiClient.getApplications(); + } + +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaSpeech.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaSpeech.java new file mode 100644 index 000000000..5a407798d --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaSpeech.java @@ -0,0 +1,130 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca; + +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +import org.opentosca.containerapi.client.model.Application; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProvider; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; +import de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationEntry; + +/** + * The speech class for the tosca plugin + * + * @author Felix Burk, Tim Neumann + */ +@Service +@SpeechCommand +public class ToscaSpeech { + + @Reference + private ToscaLogic logic; + + /** + * Speech method to lists the apps + * + * @param entities + * The speech entities. Not used. + * @return A string to output, which contains the number of installed apps and their names. + */ + @Intent + public String listApps(Map entities) { + List apps = this.logic.getInstalledApps(); + + StringBuilder ret = new StringBuilder("Found "); + + ret.append(apps.size()); + ret.append(" application"); + if (apps.size() != 1) { + ret.append('s'); + } + ret.append(". "); + + apps.forEach(a -> { + ret.append(a.getDisplayName()); + ret.append(" and "); + }); + ret.setLength(ret.length() - 5); + ret.append("."); + + return ret.toString(); + } + + /** + * Speech method o install a plugin. The name of the app and the config are in the entities under app and config + * respectively + * + * @param entities + * The speech entities to use + * @return A string to output which contains any error or a confirmation that installation is in progres + */ + @Intent + public String install(Map entities) { + String app = entities.get("app").getString(); + String config = entities.get("config").getString(); + try { + this.logic.install(app, config); + } catch (NoSuchElementException | IllegalArgumentException e) { + return e.getMessage(); + } + + return "Installing."; + } + + @Intent + public String waitForInstall(Map entities) { + this.logic.waitForInstall(); + return "finished installation"; + } + + /** + * Provides the names of the configurations + * + * @return A list of config names. + */ + @EntityProvider("config") + public List provideConfigurations() { + return this.logic.getConfigurations().stream().map(ConfigurationEntry::getTag).collect(Collectors.toList()); + } + + /** + * Provides the names of the apps + * + * @return A list of app names. + */ + @EntityProvider("app") + public List provideApps() { + return this.logic.getInstalledApps().stream().map(Application::getDisplayName).collect(Collectors.toList()); + } + +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationEntry.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationEntry.java new file mode 100644 index 000000000..5ac750edf --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationEntry.java @@ -0,0 +1,119 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations; + +import javax.persistence.*; + +import de.unistuttgart.iaas.amyassist.amy.registry.RegistryEntity; +import de.unistuttgart.iaas.amyassist.amy.registry.Taggable; + +/** + * The class representing a configuration entry + * + * @author Tim Neumann + */ +@Entity +@PersistenceUnit(unitName = "ConfigurationRegistry") +public class ConfigurationEntry implements RegistryEntity, Taggable { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(updatable = false, nullable = false) + private int persistentId; + + /** The key of the entry. */ + private String key; + /** The value of the entry. */ + private String value; + /** The tag of the entry. */ + private String tag; + + /** + * @see de.unistuttgart.iaas.amyassist.amy.registry.RegistryEntity#getPersistentId() + */ + @Override + public int getPersistentId() { + return this.persistentId; + } + + /** + * Get's {@link #key key} + * + * @return key + */ + public String getKey() { + return this.key; + } + + /** + * Set's {@link #key key} + * + * @param key + * key + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Get's {@link #value value} + * + * @return value + */ + public String getValue() { + return this.value; + } + + /** + * Set's {@link #value value} + * + * @param value + * value + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Get's {@link #tag tag} + * + * @return tag + */ + @Override + public String getTag() { + return this.tag; + } + + /** + * Set's {@link #tag tag} + * + * @param tag + * tag + */ + @Override + public void setTag(String tag) { + this.tag = tag; + } + +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationRegistry.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationRegistry.java new file mode 100644 index 000000000..6de986585 --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationRegistry.java @@ -0,0 +1,55 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.registry.AbstractTaggableRegistry; + +/** + * The configuration registry + * + * @author Tim Neumann + */ +@Service(ConfigurationRegistry.class) +public class ConfigurationRegistry extends AbstractTaggableRegistry { + + /** + * @see de.unistuttgart.iaas.amyassist.amy.registry.AbstractRegistry#getPersistenceUnitName() + */ + @Override + protected String getPersistenceUnitName() { + return "ConfigurationRegistry"; + } + + /** + * @see de.unistuttgart.iaas.amyassist.amy.registry.AbstractRegistry#getEntityClass() + */ + @Override + protected @Nonnull Class getEntityClass() { + return ConfigurationEntry.class; + } + +} diff --git a/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationResource.java b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationResource.java new file mode 100644 index 000000000..6175361b0 --- /dev/null +++ b/plugins/tosca/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/configurations/ConfigurationResource.java @@ -0,0 +1,38 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations; + +import javax.ws.rs.Path; + +import de.unistuttgart.iaas.amyassist.amy.registry.rest.AbstractRegistryResource; + +/** + * The configuration resource + * + * @author Tim Neumann + */ +@Path("tosca/configurations") +public class ConfigurationResource + extends AbstractRegistryResource { +} diff --git a/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.natlangMeta b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.natlangMeta new file mode 100644 index 000000000..26c52d9c2 --- /dev/null +++ b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.natlangMeta @@ -0,0 +1,2 @@ +.aims: +ToscaSpeech.aim.xml \ No newline at end of file diff --git a/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.properties b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.properties new file mode 100644 index 000000000..3248f8116 --- /dev/null +++ b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy-plugin-tosca.properties @@ -0,0 +1,2 @@ +CONTAINER_HOST= +CONTAINER_HOST_INTERNAL= diff --git a/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..e9ae82a6a --- /dev/null +++ b/plugins/tosca/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1,3 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.tosca.ToscaLibraryAdapter +de.unistuttgart.iaas.amyassist.amy.plugin.tosca.ToscaLogic +de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationRegistry \ No newline at end of file diff --git a/plugins/tosca/src/main/resources/META-INF/javax.ws.rs.Path b/plugins/tosca/src/main/resources/META-INF/javax.ws.rs.Path new file mode 100644 index 000000000..1bf5993be --- /dev/null +++ b/plugins/tosca/src/main/resources/META-INF/javax.ws.rs.Path @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationResource diff --git a/plugins/tosca/src/main/resources/ToscaSpeech.aim.xml b/plugins/tosca/src/main/resources/ToscaSpeech.aim.xml new file mode 100644 index 000000000..821f6fcb5 --- /dev/null +++ b/plugins/tosca/src/main/resources/ToscaSpeech.aim.xml @@ -0,0 +1,31 @@ + + + + [list] [installed] (apps|applications) + + + tell me when (finished|installed) + + + (install|deploy) + + + * + + + * + + + + Which app should I install? + {app} + + + Which config should I use? + {config} + + + diff --git a/plugins/tosca/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogicTest.java b/plugins/tosca/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogicTest.java new file mode 100644 index 000000000..673309916 --- /dev/null +++ b/plugins/tosca/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/tosca/ToscaLogicTest.java @@ -0,0 +1,168 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.tosca; + +import java.util.*; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.opentosca.containerapi.client.impl.OpenTOSCAContainerLegacyAPIClient; +import org.opentosca.containerapi.client.model.Application; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.taskscheduler.api.TaskScheduler; +import de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationEntry; +import de.unistuttgart.iaas.amyassist.amy.plugin.tosca.configurations.ConfigurationRegistry; +import de.unistuttgart.iaas.amyassist.amy.test.FrameworkExtension; +import de.unistuttgart.iaas.amyassist.amy.test.TestFramework; + +/** + * Test for the tosca logic. + * + * @author Felix Burk + */ +@ExtendWith(FrameworkExtension.class) +public class ToscaLogicTest { + + @Reference + private TestFramework framework; + + private ToscaLogic toskaLogic; + + private OpenTOSCAContainerLegacyAPIClient toscaClient; + + private List apps; + + private List createConfig() { + List config = new ArrayList<>(); + ConfigurationEntry entry1 = new ConfigurationEntry(); + entry1.setTag("testConfig"); + entry1.setKey("testKey"); + entry1.setValue("testValue"); + ConfigurationEntry entry2 = new ConfigurationEntry(); + entry2.setTag("testConfig"); + entry2.setKey("unused"); + entry2.setValue("value"); + ConfigurationEntry entry3 = new ConfigurationEntry(); + entry3.setTag("unused"); + entry3.setKey("testKey"); + entry3.setValue("wrong"); + ConfigurationEntry entry4 = new ConfigurationEntry(); + entry4.setTag("other"); + entry4.setKey("key"); + entry4.setValue("value"); + config.add(entry1); + config.add(entry2); + config.add(entry3); + config.add(entry4); + return config; + } + + private List createApps() { + List ret = new ArrayList<>(); + ret.add(new Application("test", Arrays.asList("testKey"), Collections.emptyList(), "Test App", "V1", + "A test app.", "tester", Collections.emptyList(), "meta")); + return ret; + } + + /** + * Setup + * + * @throws Exception + * When anything goes wrong + */ + @BeforeEach + public void setup() throws Exception { + this.framework.mockService(Properties.class); + TaskScheduler scheduler = this.framework.mockService(TaskScheduler.class); + Mockito.doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Runnable run = (Runnable) invocation.getArguments()[0]; + run.run(); + return null; + } + }).when(scheduler).execute(ArgumentMatchers.any()); + + ConfigurationRegistry registry = this.framework.mockService(ConfigurationRegistry.class); + List config = createConfig(); + Mockito.when(registry.getAll()).thenReturn(config); + this.toscaClient = Mockito.mock(OpenTOSCAContainerLegacyAPIClient.class); + this.apps = createApps(); + Mockito.when(this.toscaClient.getApplications()).thenReturn(this.apps); + + ToscaLibraryAdapter adap = this.framework.mockService(ToscaLibraryAdapter.class); + Mockito.when(adap.createLibrary(null, null)).thenReturn(this.toscaClient); + + this.toskaLogic = this.framework.setServiceUnderTest(ToscaLogic.class); + } + + /** + * Test {@link ToscaLogic#getInstalledApps()} + */ + @Test + public void testGetInstalledApps() { + Assertions.assertEquals(this.apps, this.toskaLogic.getInstalledApps()); + } + + /** + * Tests {@link ToscaLogic#install(String, String)} and through that the other variants of that method in the normal + * case. + */ + @Test + public void testInstall() { + this.toskaLogic.install("Test App", "testConfig"); + Map args = new HashMap<>(); + args.put("testKey", "testValue"); + Mockito.verify(this.toscaClient).createServiceInstance(ArgumentMatchers.eq(this.apps.get(0)), + ArgumentMatchers.eq(args)); + } + + /** + * Tests {@link ToscaLogic#install(String, String)} when the given name does not correspond to an app. + */ + @Test + public void testInstallNoSuchApp() { + Assertions.assertThrows(NoSuchElementException.class, + () -> this.toskaLogic.install("Not an app", "testConfig")); + } + + /** + * Tests {@link ToscaLogic#install(String, String)} and through that {@link ToscaLogic#install(Application, String)} + * when the given configuration does not contain all keys required. + */ + @Test + public void testInstallConfigWrong() { + Assertions.assertThrows(IllegalArgumentException.class, + () -> this.toskaLogic.install("Test App", "not a config")); + Assertions.assertThrows(IllegalArgumentException.class, () -> this.toskaLogic.install("Test App", "other")); + } + +} diff --git a/plugins/weather/pom.xml b/plugins/weather/pom.xml index a85ea2c01..7e2ea3b3d 100644 --- a/plugins/weather/pom.xml +++ b/plugins/weather/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy-plugin - 0.5.0 + 0.6.0 ../pom.xml diff --git a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherDarkSkyAdapter.java b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherDarkSkyAdapter.java index ff02880d3..a37b19697 100644 --- a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherDarkSkyAdapter.java +++ b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherDarkSkyAdapter.java @@ -90,7 +90,7 @@ WeatherReport getWeatherReport(String latitude, String longitude) { JsonObject instant = fio.getCurrently(); JsonObject week = fio.getDaily(); - return new WeatherReport(convertInstant(instant), convertWeek(week), fio.getTimezone()); + return new WeatherReport(convertInstant(instant), convertWeek(week, fio.getTimezone()), fio.getTimezone()); } private WeatherReportInstant convertInstant(JsonObject toConvert) { @@ -105,18 +105,18 @@ private WeatherReportInstant convertInstant(JsonObject toConvert) { timestamp, windSpeed, trimQuotes(iconType)); } - private WeatherReportWeek convertWeek(JsonObject toConvert) { + private WeatherReportWeek convertWeek(JsonObject toConvert, String timezone) { JsonArray arr = toConvert.get(JSON_NAME_DAYS).asArray(); int size = arr.size(); WeatherReportDay[] days = new WeatherReportDay[size]; for (int i = 0; i < size; i++) { - days[i] = convertDay(arr.get(i).asObject()); + days[i] = convertDay(arr.get(i).asObject(), timezone); } return new WeatherReportWeek(toConvert.get(JSON_NAME_SUMMARY).asString(), days); } - private WeatherReportDay convertDay(JsonObject toConvert) { + private WeatherReportDay convertDay(JsonObject toConvert, String timezone) { String summary = toConvert.getString(JSON_NAME_SUMMARY, "null"); double precipProbability = toConvert.getDouble(JSON_NAME_PERCIP_PROB, Double.NaN); String precipType = toConvert.getString(JSON_NAME_PERCIP_TYPE, "null"); @@ -129,7 +129,7 @@ private WeatherReportDay convertDay(JsonObject toConvert) { String iconType = toConvert.getString(JSON_NAME_ICON_TYPE, "null"); return new WeatherReportDay(trimQuotes(summary), precipProbability, trimQuotes(precipType), temperatureMin, - temperatureMax, timestamp, sunrise, sunset, windSpeed, trimQuotes(iconType)); + temperatureMax, timestamp, sunrise, sunset, windSpeed, trimQuotes(iconType), timezone); } private String trimQuotes(String s) { diff --git a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherReportDay.java b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherReportDay.java index 79f9df4eb..0918e5d15 100644 --- a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherReportDay.java +++ b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherReportDay.java @@ -23,6 +23,13 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.weather; +import de.unistuttgart.iaas.amyassist.amy.utility.rest.adapter.ZonedDateTimeAdapter; + +import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; + /** * Weather report for a day * @@ -40,7 +47,8 @@ public class WeatherReportDay { /** The maximal temperature in degrees celcius */ private final double temperatureMax; /** The time stamp of the weather report */ - private final long timestamp; + @XmlJavaTypeAdapter(ZonedDateTimeAdapter.class) + private final ZonedDateTime timestamp; /** The time stamp of the sunrise */ private final long sunriseTime; /** The time stamp of the sunset */ @@ -81,13 +89,13 @@ public class WeatherReportDay { */ public WeatherReportDay(String pSummary, double pPrecipitationProbability, String pPrecipitationType, double pTemperatureMin, double pTemperatureMax, long pTimestamp, long pSunriseTime, long pSunsetTime, - double pWindSpeed, String pIconType) { + double pWindSpeed, String pIconType, String pTimezone) { this.summary = pSummary; this.precipProbability = pPrecipitationProbability; this.precipType = pPrecipitationType; this.temperatureMin = pTemperatureMin; this.temperatureMax = pTemperatureMax; - this.timestamp = pTimestamp; + this.timestamp = ZonedDateTime.ofInstant(Instant.ofEpochSecond(pTimestamp), ZoneId.of(pTimezone)); this.sunriseTime = pSunriseTime; this.sunsetTime = pSunsetTime; this.windSpeed = pWindSpeed; @@ -144,7 +152,7 @@ public double getTemperatureMax() { * * @return timestamp */ - public long getTimestamp() { + public ZonedDateTime getTimestamp() { return this.timestamp; } diff --git a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherSpeechCommand.java b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherSpeechCommand.java index e5f02dff5..03cb3dec8 100644 --- a/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherSpeechCommand.java +++ b/plugins/weather/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherSpeechCommand.java @@ -23,28 +23,28 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.weather; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.time.DayOfWeek; -import java.time.Instant; -import java.time.ZoneId; -import java.time.ZonedDateTime; -import java.util.*; -import javax.annotation.Nullable; +import org.apache.commons.lang3.tuple.Pair; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityData; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.EntityProvider; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.Intent; -import de.unistuttgart.iaas.amyassist.amy.core.natlang.SpeechCommand; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.*; import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; import de.unistuttgart.iaas.amyassist.amy.plugin.weather.WeatherLogic.GeoCoordinatePair; import de.unistuttgart.iaas.amyassist.amy.registry.Location; import de.unistuttgart.iaas.amyassist.amy.registry.LocationRegistry; import de.unistuttgart.iaas.amyassist.amy.registry.geocoder.Geocoder; import de.unistuttgart.iaas.amyassist.amy.registry.geocoder.GeocoderException; -import org.apache.commons.lang3.tuple.Pair; + +import javax.annotation.Nullable; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.time.DayOfWeek; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.TimeZone; /** * @@ -109,6 +109,16 @@ private String stringifyDayWeatherReport(String preamble, WeatherReportDay repor return result; } + private String stringifyInstantWeatherReport(String preamble, WeatherReportInstant report) { + String result = preamble + " " + report.getSummary() + "."; + + result += " Currently " + round(report.getTemperature()) + "°C."; + + result += " Wind speed is about " + round(report.getWindSpeed()) + " meters per second"; + + return result; + } + private String stringifyWeekWeatherReport(String preamble, WeatherReportWeek report) { return preamble + " " + report.getSummary(); } @@ -195,8 +205,7 @@ public String weatherWeekend(Map entities) { String saturdayReport = null; String sundayReport = null; for (WeatherReportDay d : report.getWeek().getDays()) { - Instant instant = Instant.ofEpochSecond(d.getTimestamp()); - ZonedDateTime date = ZonedDateTime.ofInstant(instant, ZoneId.of(report.getTimezone())); + ZonedDateTime date = d.getTimestamp(); DayOfWeek day = date.getDayOfWeek(); if (day == DayOfWeek.SATURDAY) { saturdayReport = stringifyDayWeatherReport("", d, report.getTimezone(), true); @@ -208,6 +217,72 @@ public String weatherWeekend(Map entities) { } } + /** + * tells user if its gonna rain today / tomorrow etc + * + * @param entities + * information + * @return the answer + */ + @Intent + public String rainCheck(Map entities) { + WeatherReport report; + if (entities.get("locationname") != null) { + String locationName = entities.get("locationname").getString(); + try { + Pair coordinates = this.geocoder.geocodeAddress(locationName); + GeoCoordinatePair pair = new GeoCoordinatePair(coordinates.getRight(), coordinates.getLeft()); + report = this.weatherLogic.getWeatherReport(pair); + } catch (GeocoderException e) { + return "I couldn't find this location"; + } + } else { + GeoCoordinatePair curr = getCurrentLocation(); + if (curr == null) + return NO_LOCATION_STRING; + report = this.weatherLogic.getWeatherReport(curr); + } + + String timespan = entities.get("timespan").getString(); + if (timespan.equals("today")) { + WeatherReportDay today = report.getWeek().getDays()[0]; + return stringifyRainCheck(today.getPrecipProbability(), today.getPrecipType(), "today"); + + } else if (timespan.equals("tomorrow")) { + WeatherReportDay tomorrow = report.getWeek().getDays()[1]; + return stringifyRainCheck(tomorrow.getPrecipProbability(), tomorrow.getPrecipType(), "tomorrow"); + + } else if (timespan.equals("on the weekend")) { + StringBuilder builder = new StringBuilder(); + String sat = ""; + String sun = ""; + for (WeatherReportDay d : report.getWeek().getDays()) { + ZonedDateTime date = d.getTimestamp(); + DayOfWeek day = date.getDayOfWeek(); + if (day == DayOfWeek.SATURDAY) { + sat = stringifyRainCheck(d.getPrecipProbability(), d.getPrecipType(), "on Saturday"); + } else if (day == DayOfWeek.SUNDAY) { + sun = stringifyRainCheck(d.getPrecipProbability(), d.getPrecipType(), "on Sunday"); + } + } + builder.append(sat + "\n" + sun); + return builder.toString(); + } + + return "i don't understand the date"; + } + + private String stringifyRainCheck(double probability, String precipType, String suffix) { + final String chanceOf = "% chance of "; + + if(round(probability) == 0) { + return "0% chance of rain " + suffix + "!"; + } + + return round(probability * 100) + chanceOf + precipType + " " + suffix + "!"; + + } + /** * speech command to set a new weather location. only registry entries are allowed * @@ -240,17 +315,23 @@ public List getAllLocationTags() { return locationNames; } + + @Intent() - public String weatherAtLocation(Map entities) { + public Response weatherAtLocation(Map entities) { String locationName = entities.get("locationname").getString(); try { - Pair coordinates = geocoder.geocodeAddress(locationName); + Pair coordinates = this.geocoder.geocodeAddress(locationName); GeoCoordinatePair pair = new GeoCoordinatePair(coordinates.getRight(), coordinates.getLeft()); + WeatherReport report = weatherLogic.getWeatherReport(pair); - return stringifyDayWeatherReport("This is the weather report for " + locationName + ". ", - report.getWeek().getDays()[0], report.getTimezone(), false); + + WeatherReportInstant instantReport = report.getCurrent(); + String text = stringifyInstantWeatherReport("This is the weather report for " + locationName + ". ", + instantReport); + return Response.text(text).widget("app-weather-day").attachment(instantReport).build(); } catch (GeocoderException e) { - return "I couldn't find this location"; + return Response.text("I couldn't find this location").build(); } } } diff --git a/plugins/weather/src/main/resources/WeatherSpeech.aim.xml b/plugins/weather/src/main/resources/WeatherSpeech.aim.xml index 9c2af02e7..e7f4c60b6 100644 --- a/plugins/weather/src/main/resources/WeatherSpeech.aim.xml +++ b/plugins/weather/src/main/resources/WeatherSpeech.aim.xml @@ -2,39 +2,51 @@ - [how is the] weather today + [how is the] weather [like] today - [how is the] weather tomorrow + [how is the] weather [like] tomorrow - [how is the] weather [for the next] week + [how is the] weather [like] [for the next] week - [how is the] weather [at the] weekend + [how is the] weather [on the] weekend + + + [(will|is) it (gonna|going to)] rain ({timespan} [(at|in) {locationname}]|[(at|in) {locationname}] {timespan}) + + + (today|tomorrow|on the weekend) + + + + + + - set weather location [to {weatherlocation}] + (set|change) weather location [to {weatherlocation}] loc - To which location should i switch? - [to location] {weatherlocation} + To which location should I switch? + [to] [location] {weatherlocation} - [(whats|what is)] [the] weather [like] in {locationname} + [whats|what (is|s)|how is] [the] weather [like] in {locationname} * - \ No newline at end of file + diff --git a/plugins/weather/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherTestUtil.java b/plugins/weather/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherTestUtil.java index 81610850d..8ee308009 100644 --- a/plugins/weather/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherTestUtil.java +++ b/plugins/weather/src/test/java/de/unistuttgart/iaas/amyassist/amy/plugin/weather/WeatherTestUtil.java @@ -23,6 +23,7 @@ package de.unistuttgart.iaas.amyassist.amy.plugin.weather; +import java.time.Instant; import java.util.Random; import java.util.TimeZone; @@ -94,8 +95,8 @@ public static WeatherReport generateWeatherReport(Random rng) { */ public static WeatherReportDay generateWeatherReportDay(Random rng) { return new WeatherReportDay(randomString(rng, rng.nextInt(40)), rng.nextDouble(), rPerType(rng), - randomDoubleWithNegative(rng, 40), randomDoubleWithNegative(rng, 40), rng.nextLong(), rng.nextLong(), - rng.nextLong(), randomDouble(rng, 50), rIconType(rng)); + randomDoubleWithNegative(rng, 40), randomDoubleWithNegative(rng, 40), Instant.now().getEpochSecond(), rng.nextLong(), + rng.nextLong(), randomDouble(rng, 50), rIconType(rng), "Europe/Berlin"); } /** diff --git a/plugins/webpush/.settings/org.eclipse.jdt.core.prefs b/plugins/webpush/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..47397d9d2 --- /dev/null +++ b/plugins/webpush/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,431 @@ +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=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=javax.annotation.ParametersAreNonnullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +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.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.problem.APILeak=warning +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=warning +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=warning +org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=warning +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=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=warning +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=enabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.localVariableHiding=warning +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=warning +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=default +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=all_standard_tags +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=warning +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=enabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=private +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=warning +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=warning +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=error +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning +org.eclipse.jdt.core.compiler.problem.potentialNullReference=error +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=warning +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=warning +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedParameter=warning +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=warning +org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning +org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=120 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=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=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_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_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_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_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=mixed +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/plugins/webpush/.settings/org.eclipse.jdt.ui.prefs b/plugins/webpush/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 000000000..81e418a00 --- /dev/null +++ b/plugins/webpush/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,67 @@ +cleanup.add_default_serial_version_id=false +cleanup.add_generated_serial_version_id=true +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=true +cleanup.always_use_blocks=false +cleanup.always_use_parentheses_in_expressions=true +cleanup.always_use_this_for_non_static_field_access=true +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=true +cleanup.format_source_code=true +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=false +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=true +cleanup.remove_trailing_whitespaces=true +cleanup.remove_trailing_whitespaces_all=false +cleanup.remove_trailing_whitespaces_ignore_empty=true +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=true +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=true +cleanup.use_this_for_non_static_field_access_only_if_necessary=false +cleanup.use_this_for_non_static_method_access=true +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_Amy-CleanUp +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_Amy-Formatter +formatter_settings_version=13 +org.eclipse.jdt.ui.ignorelowercasenames=true +org.eclipse.jdt.ui.importorder=java;javax;org;com; +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.ondemandthreshold=5 +org.eclipse.jdt.ui.staticondemandthreshold=1 +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/plugins/webpush/.settings/org.eclipse.m2e.core.prefs b/plugins/webpush/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/plugins/webpush/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/plugins/webpush/pom.xml b/plugins/webpush/pom.xml new file mode 100644 index 000000000..980de4fcc --- /dev/null +++ b/plugins/webpush/pom.xml @@ -0,0 +1,81 @@ + + 4.0.0 + amy-web-push + Amy push notification plugin + This plugin can send push notification to registered WebApps + + de.unistuttgart.iaas.amyassist + amy-plugin + 0.6.0 + ../pom.xml + + + + nl.martijndwars + web-push + 3.1.1 + + + com.fasterxml.jackson.core + jackson-databind + 2.9.6 + + + + + + org.bouncycastle + bcprov-jdk15on + 1.54 + provided + + + org.apache.httpcomponents + httpasyncclient + 4.1.4 + + + + + + + maven-shade-plugin + 3.1.1 + + + + + + + lib/bcprov-jdk15on-1.54.jar + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.1.1 + + + copy-dependencies + package + + copy-dependencies + + + bcprov-jdk15on + ${project.parent.parent.basedir}/${amy.plugin.build.dir}/lib + + + + + + + diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushService.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushService.java new file mode 100644 index 000000000..b348ec2b4 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushService.java @@ -0,0 +1,55 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Notification; + +/** + * Simple abstracted WebPushService interface. + * + * @author Leon Kiefer + */ +public interface SimpleWebPushService { + + /** + * Send a Push notification to the single client with the given unique id. + * + * @param id + * the id of the client subscription + * @param notification + * the notification to send + */ + void sendPushNotification(int id, Notification notification); + + /** + * Send a Push notification to the clients with the given name. Multiple clients can have the same name. + * + * @param name + * the name of the clients subscription + * @param notification + * the notification to send + */ + void sendPushNotification(String name, Notification notification); + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushServiceImpl.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushServiceImpl.java new file mode 100644 index 000000000..53a21ef35 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/SimpleWebPushServiceImpl.java @@ -0,0 +1,72 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Notification; + +/** + * Implementation of the simple facade. + * + * @author Leon Kiefer + */ +@Service +public class SimpleWebPushServiceImpl implements SimpleWebPushService { + @Reference + private WebPushService webPushService; + + @Reference + private WebPushNameService webPushNameService; + + @Override + public void sendPushNotification(int id, Notification notification) { + this.webPushService.sendPushNotification(id, this.marshallNotification(notification)); + } + + @Override + public void sendPushNotification(String name, Notification notification) { + this.webPushNameService.sendPushNotification(name, this.marshallNotification(notification)); + } + + private byte[] marshallNotification(Notification notification) { + ObjectMapper objectMapper = new ObjectMapper(); + try { + return objectMapper.writeValueAsBytes(new Payload(notification)); + } catch (JsonProcessingException e) { + throw new IllegalArgumentException(e); + } + } + + private class Payload { + public Notification notification; + + public Payload(Notification notification) { + this.notification = notification; + } + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameService.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameService.java new file mode 100644 index 000000000..60355ba15 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameService.java @@ -0,0 +1,52 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import java.util.List; + +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Subscription; + +/** + * Use the WebPushService with names. + * + * @author Leon Kiefer + */ +public interface WebPushNameService { + int subscribe(Subscription subscription, String name); + + String getName(int id); + + void setName(int id, String name); + + List getIDs(String name); + + List getAllIDs(); + + /** + * @param name + * @param payload + */ + void sendPushNotification(String name, byte[] payload); + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameServiceImpl.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameServiceImpl.java new file mode 100644 index 000000000..d632ad5ab --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushNameServiceImpl.java @@ -0,0 +1,96 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import java.util.List; +import java.util.NoSuchElementException; +import java.util.stream.Collectors; + +import org.slf4j.Logger; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Subscription; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence.SubscriptionEntity; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence.SubscriptionStorage; + +/** + * Implementation of the WebPushNameService. This uses Tags of the Database entity as names. + * + * @author Leon Kiefer + */ +@Service +public class WebPushNameServiceImpl implements WebPushNameService { + + @Reference + private Logger logger; + + @Reference + private SubscriptionStorage subscriptionStorage; + + @Reference + private WebPushService webPushService; + + @Override + public void sendPushNotification(String name, byte[] payload) { + for (int id : this.getIDs(name)) { + this.webPushService.sendPushNotification(id, payload); + } + } + + @Override + public int subscribe(Subscription subscription, String name) { + int subscribe = this.webPushService.subscribe(subscription); + this.setName(subscribe, name); + return subscribe; + } + + @Override + public String getName(int id) { + return this.subscriptionStorage.getById(id).getTag(); + } + + @Override + public List getIDs(String name) { + return this.subscriptionStorage.getEntitiesWithTag(name).stream().map(SubscriptionEntity::getPersistentId) + .collect(Collectors.toList()); + } + + @Override + public List getAllIDs() { + return this.subscriptionStorage.getAll().stream().map(SubscriptionEntity::getPersistentId) + .collect(Collectors.toList()); + } + + @Override + public void setName(int id, String name) { + SubscriptionEntity entity = this.subscriptionStorage.getById(id); + if (entity == null) { + throw new NoSuchElementException("No Subscription with id: " + id); + } + entity.setTag(name); + this.subscriptionStorage.save(entity); + } + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushService.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushService.java new file mode 100644 index 000000000..16c89dabf --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushService.java @@ -0,0 +1,57 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import java.util.NoSuchElementException; + +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Subscription; + +/** + * The Core Service of this plugin. + * + * @author Leon Kiefer + */ +public interface WebPushService { + int subscribe(Subscription subscription); + + /** + * @param id + * the id of the subscription + * @throws NoSuchElementException + * if no subscription found for the given id + */ + void unsubscribe(int id); + + String getPublicVAPIDKey(); + + /** + * Send a Push notification to a single client with the given id. + * + * @param id + * the unique id of the client subscription + * @param payload + * the notification payload + */ + void sendPushNotification(int id, byte[] payload); +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushServiceImpl.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushServiceImpl.java new file mode 100644 index 000000000..2fffef837 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/WebPushServiceImpl.java @@ -0,0 +1,180 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush; + +import static nl.martijndwars.webpush.Utils.*; +import static org.bouncycastle.jce.provider.BouncyCastleProvider.*; + +import java.io.IOException; +import java.security.*; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import java.util.NoSuchElementException; +import java.util.concurrent.Future; + +import org.apache.http.HttpResponse; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.interfaces.ECPrivateKey; +import org.bouncycastle.jce.interfaces.ECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.math.ec.ECPoint; +import org.jose4j.lang.JoseException; +import org.slf4j.Logger; + +import com.google.common.io.BaseEncoding; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.PostConstruct; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Subscription; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence.SubscriptionEntity; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence.SubscriptionStorage; +import nl.martijndwars.webpush.Notification; +import nl.martijndwars.webpush.PushService; +import nl.martijndwars.webpush.Utils; + +/** + * Implementation of the WebPushService using the BouncyCastleProvider for encryption. + * + * @author Leon Kiefer + */ +@Service +public class WebPushServiceImpl implements WebPushService { + + private static final String PUBLIC_KEY = "PublicKey"; + private static final String PRIVATE_KEY = "PrivateKey"; + + @Reference + private Logger logger; + + /** + * A reference to the storage. + */ + @Reference + private IStorage storage; + @Reference + private SubscriptionStorage subscriptionStorage; + + @PostConstruct + private void setup() { + Security.addProvider(new BouncyCastleProvider()); + + if (!(this.storage.has(PUBLIC_KEY) && this.storage.has(PRIVATE_KEY))) { + this.logger.info("Create new key pair"); + try { + KeyPair keyPair = this.generateKeyPair(); + + byte[] publicKey = Utils.savePublicKey((ECPublicKey) keyPair.getPublic()); + byte[] privateKey = Utils.savePrivateKey((ECPrivateKey) keyPair.getPrivate()); + + this.storage.put(PUBLIC_KEY, BaseEncoding.base64Url().encode(publicKey)); + this.storage.put(PRIVATE_KEY, BaseEncoding.base64Url().encode(privateKey)); + } catch (InvalidAlgorithmParameterException | NoSuchProviderException | NoSuchAlgorithmException e) { + this.logger.error("Could not generate key pair", e); + } + } + + } + + /** + * Generate an EC keypair on the prime256v1 curve. + * + * @return the generated key pair + * @throws InvalidAlgorithmParameterException + * @throws NoSuchProviderException + * @throws NoSuchAlgorithmException + */ + private KeyPair generateKeyPair() + throws InvalidAlgorithmParameterException, NoSuchProviderException, NoSuchAlgorithmException { + ECNamedCurveParameterSpec parameterSpec = ECNamedCurveTable.getParameterSpec(CURVE); + + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM, PROVIDER_NAME); + keyPairGenerator.initialize(parameterSpec); + + return keyPairGenerator.generateKeyPair(); + } + + private Future sendPushMessage(SubscriptionEntity sub, byte[] payload) { + try { + Notification notification = new Notification(sub.getEndpoint(), this.getUserPublicKey(sub), + this.getAuthAsBytes(sub), payload); + String publicKey = this.storage.get(PUBLIC_KEY); + String privateKey = this.storage.get(PRIVATE_KEY); + PushService pushService = new PushService(publicKey, privateKey, "testSubject"); + return pushService.sendAsync(notification); + } catch (GeneralSecurityException | IOException | JoseException e) { + throw new IllegalStateException(e); + } + } + + private PublicKey getUserPublicKey(SubscriptionEntity sub) + throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchProviderException { + KeyFactory kf = KeyFactory.getInstance("ECDH", BouncyCastleProvider.PROVIDER_NAME); + ECNamedCurveParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1"); + + byte[] decode = Base64.getUrlDecoder().decode(sub.getKey()); + + ECPoint point = ecSpec.getCurve().decodePoint(decode); + ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, ecSpec); + + return kf.generatePublic(pubSpec); + } + + private byte[] getAuthAsBytes(SubscriptionEntity sub) { + return Base64.getUrlDecoder().decode(sub.getAuth()); + } + + @Override + public int subscribe(Subscription subscription) { + SubscriptionEntity subscriptionEntity = new SubscriptionEntity(subscription.getEndpoint(), + subscription.getAuth(), subscription.getKey()); + this.subscriptionStorage.save(subscriptionEntity); + return subscriptionEntity.getPersistentId(); + } + + @Override + public void unsubscribe(int id) { + if (this.subscriptionStorage.getById(id) == null) { + throw new NoSuchElementException("No Subscription with id: " + id); + } + this.subscriptionStorage.deleteById(id); + } + + @Override + public String getPublicVAPIDKey() { + return this.storage.get(PUBLIC_KEY); + } + + @Override + public void sendPushNotification(int id, byte[] payload) { + SubscriptionEntity subscriptionEntity = this.subscriptionStorage.getById(id); + if (subscriptionEntity == null) { + throw new NoSuchElementException("No Subscription with id: " + id); + } + this.sendPushMessage(subscriptionEntity, payload); + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/message/NotificationMessageReceiver.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/message/NotificationMessageReceiver.java new file mode 100644 index 000000000..80a1dda32 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/message/NotificationMessageReceiver.java @@ -0,0 +1,66 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.message; + +import java.util.Collections; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceiver; +import de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.Subscription; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topic.TopicName; +import de.unistuttgart.iaas.amyassist.amy.messagehub.topics.SystemTopics; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.SimpleWebPushService; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushNameService; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.Notification; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.NotificationImp; + +/** + * Send Push notification for messages on the user notification topic + * + * @author Leon Kiefer + */ +@MessageReceiver +public class NotificationMessageReceiver { + + @Reference + private SimpleWebPushService simpleWebPushService; + + @Reference + private WebPushNameService webPushNameService; + + @Subscription(SystemTopics.USER + "/+/notification") + public void notifyUser(String message, TopicName topic) { + String userName = topic.getTopicLevels().get(1).getStringRepresentation(); + + Notification notification = new NotificationImp("Amy Message Notification", message, + "assets/icons/icon-512x512.png", new int[] { 100, 50, 100 }, "", Collections.emptyList()); + if (userName.equals("all")) { + for (int id : this.webPushNameService.getAllIDs()) { + this.simpleWebPushService.sendPushNotification(id, notification); + } + } else { + this.simpleWebPushService.sendPushNotification(userName, notification); + } + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Notification.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Notification.java new file mode 100644 index 000000000..f174fa3ce --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Notification.java @@ -0,0 +1,51 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model; + +import java.util.List; + +/** + * Notification model definition + * + * @author Leon Kiefer + */ +public interface Notification { + String getTitle(); + + String getBody(); + + String getIcon(); + + int[] getVibrate(); + + String getData(); + + List getActions(); + + public static interface Action { + String getAction(); + + String getTitle(); + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/NotificationImp.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/NotificationImp.java new file mode 100644 index 000000000..a7789d36b --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/NotificationImp.java @@ -0,0 +1,114 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model; + +import java.util.List; + +/** + * Implementation of the Notification model. + * + * @author Leon Kiefer + */ +public class NotificationImp implements Notification { + private final String title; + private final String body; + private final String icon; + private final int[] vibrate; + private final String data; + private final List actions; + + /** + * @param title + * the title of the notification + * @param body + * text body of the notification + * @param icon + * the url of the icon to display + * @param vibrate + * @param data + * custom data + * @param actions + * possible user actions for this notification + */ + public NotificationImp(String title, String body, String icon, int[] vibrate, String data, List actions) { + this.title = title; + this.body = body; + this.icon = icon; + this.vibrate = vibrate; + this.data = data; + this.actions = actions; + } + + @Override + public String getTitle() { + return this.title; + } + + @Override + public String getBody() { + return this.body; + } + + @Override + public String getIcon() { + return this.icon; + } + + @Override + public int[] getVibrate() { + return this.vibrate; + } + + @Override + public String getData() { + return this.data; + } + + @Override + public List getActions() { + return this.actions; + } + + public static class ActionImpl implements Action { + private final String action; + private final String title; + + public ActionImpl(String action, String title) { + this.action = action; + this.title = title; + } + + @Override + public String getAction() { + return this.action; + } + + @Override + public String getTitle() { + return this.title; + } + + } + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Subscription.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Subscription.java new file mode 100644 index 000000000..904f802a5 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/Subscription.java @@ -0,0 +1,45 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model; + +/** + * Subscription data definition. + * + * @author Leon Kiefer + */ +public interface Subscription { + + String getEndpoint(); + + void setEndpoint(String endpoint); + + String getKey(); + + void setKey(String key); + + String getAuth(); + + void setAuth(String auth); + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/SubscriptionImpl.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/SubscriptionImpl.java new file mode 100644 index 000000000..59f9ba790 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/model/SubscriptionImpl.java @@ -0,0 +1,72 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model; + +/** + * Implementation of Subscription. + * + * @author Leon Kiefer + */ +public class SubscriptionImpl implements Subscription { + + private String endpoint; + private String auth; + private String key; + + public SubscriptionImpl(String endpoint, String auth, String key) { + this.endpoint = endpoint; + this.auth = auth; + this.key = key; + } + + @Override + public void setAuth(String auth) { + this.auth = auth; + } + + @Override + public String getAuth() { + return this.auth; + } + + @Override + public void setKey(String key) { + this.key = key; + } + + @Override + public String getKey() { + return this.key; + } + + @Override + public String getEndpoint() { + return this.endpoint; + } + + @Override + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionEntity.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionEntity.java new file mode 100644 index 000000000..b1bafcdb9 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionEntity.java @@ -0,0 +1,96 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence; + +import javax.persistence.*; + +import de.unistuttgart.iaas.amyassist.amy.registry.RegistryEntity; +import de.unistuttgart.iaas.amyassist.amy.registry.Taggable; + +/** + * Database entity class. + * + * @author Leon Kiefer + */ +@Entity +@PersistenceUnit(unitName = "push-subscriptions") +public class SubscriptionEntity implements RegistryEntity, Taggable { + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(updatable = false, nullable = false) + private int id; + private String tag; + private String endpoint; + private String auth; + private String key; + + public SubscriptionEntity() { + // empty default constructor + } + + public SubscriptionEntity(String endpoint, String auth, String key) { + this.endpoint = endpoint; + this.auth = auth; + this.key = key; + } + + public int getPersistentId() { + return this.id; + } + + public void setAuth(String auth) { + this.auth = auth; + } + + public String getAuth() { + return this.auth; + } + + public void setKey(String key) { + this.key = key; + } + + public String getKey() { + return this.key; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getEndpoint() { + return this.endpoint; + } + + @Override + public String getTag() { + return this.tag; + } + + @Override + public void setTag(String tag) { + this.tag = tag; + } + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionStorage.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionStorage.java new file mode 100644 index 000000000..90c0d4026 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/persistence/SubscriptionStorage.java @@ -0,0 +1,49 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence; + +import javax.annotation.Nonnull; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.registry.AbstractTaggableRegistry; + +/** + * The persistence implementation + * + * @author Leon Kiefer + */ +@Service(SubscriptionStorage.class) +public class SubscriptionStorage extends AbstractTaggableRegistry { + + @Override + protected String getPersistenceUnitName() { + return "push-subscriptions"; + } + + @Override + protected @Nonnull Class getEntityClass() { + return SubscriptionEntity.class; + } + +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/Subscription.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/Subscription.java new file mode 100644 index 000000000..bdadb3f68 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/Subscription.java @@ -0,0 +1,49 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.rest; + +import java.net.URI; + +/** + * The PushSubscription from the javascript Push API https://developer.mozilla.org/en-US/docs/Web/API/PushSubscription. + * This is a DTO class. + * + * @author Leon Kiefer + */ +public class Subscription { + public URI endpoint; + public String expirationTime; + public Keys keys; + + public class Keys { + /** + * Base64 URL encoded String + */ + public String p256dh; + /** + * Base64 URL encoded String + */ + public String auth; + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushNotificationRescource.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushNotificationRescource.java new file mode 100644 index 000000000..572490982 --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushNotificationRescource.java @@ -0,0 +1,72 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.rest; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushNameService; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushService; + +/** + * Resource class for sending Push Notifications. + * + * @author Leon Kiefer + */ +@Path("webpush/notification") +public class WebPushNotificationRescource { + @Reference + private WebPushService webPushService; + + @Reference + private WebPushNameService webPushNameService; + + /** + * + * @param id + * the id of the subscription + * @param json + * the payload of the notification as json encoded object + */ + @POST + @Path("{id}") + @Consumes(MediaType.APPLICATION_JSON) + public void sendPushNotification(@PathParam("id") int id, byte[] json) { + this.webPushService.sendPushNotification(id, json); + } + + /** + * + * @param name + * the name of the subscription + * @param json + * the payload of the notification as json encoded object + */ + @POST + @Consumes(MediaType.APPLICATION_JSON) + public void sendPushNotification(@QueryParam("clientName") String name, byte[] json) { + this.webPushNameService.sendPushNotification(name, json); + } +} diff --git a/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushSubscriptionRescource.java b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushSubscriptionRescource.java new file mode 100644 index 000000000..1f406068a --- /dev/null +++ b/plugins/webpush/src/main/java/de/unistuttgart/iaas/amyassist/amy/plugin/webpush/rest/WebPushSubscriptionRescource.java @@ -0,0 +1,89 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.plugin.webpush.rest; + +import java.util.NoSuchElementException; + +import javax.ws.rs.*; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response.Status; + +import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushNameService; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushService; +import de.unistuttgart.iaas.amyassist.amy.plugin.webpush.model.SubscriptionImpl; + +/** + * Resource class to setup Subscriptions. + * + * @author Leon Kiefer + */ +@Path("webpush/subscription") +public class WebPushSubscriptionRescource { + @Reference + private WebPushService webPushService; + + @Reference + private WebPushNameService webPushNameService; + + /** + * + * @return Base64 URL encoded public VAPID key + */ + @GET + @Path("key") + @Produces(MediaType.TEXT_PLAIN) + public String getPublicVAPIDKey() { + return this.webPushService.getPublicVAPIDKey(); + } + + @POST + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.TEXT_PLAIN) + public int subscribe(@QueryParam("clientName") String name, Subscription subscription) { + return this.webPushNameService.subscribe(new SubscriptionImpl(subscription.endpoint.toString(), + subscription.keys.auth, subscription.keys.p256dh), name); + } + + @DELETE + @Path("{id}") + public void unsubscribe(@PathParam("id") int id) { + try { + this.webPushService.unsubscribe(id); + } catch (NoSuchElementException e) { + throw new WebApplicationException(e, Status.NOT_FOUND); + } + } + + @POST + @Path("{id}") + @Consumes(MediaType.TEXT_PLAIN) + public void setName(@PathParam("id") int id, String name) { + try { + this.webPushNameService.setName(id, name); + } catch (NoSuchElementException e) { + throw new WebApplicationException(e, Status.NOT_FOUND); + } + } +} diff --git a/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services b/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services new file mode 100644 index 000000000..34c0f5ecb --- /dev/null +++ b/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.core.di.Services @@ -0,0 +1,4 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushServiceImpl +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.WebPushNameServiceImpl +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.persistence.SubscriptionStorage +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.SimpleWebPushServiceImpl diff --git a/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers b/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers new file mode 100644 index 000000000..b1149704b --- /dev/null +++ b/plugins/webpush/src/main/resources/META-INF/de.unistuttgart.iaas.amyassist.amy.messagehub.annotations.MessageReceivers @@ -0,0 +1 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.message.NotificationMessageReceiver diff --git a/plugins/webpush/src/main/resources/META-INF/javax.ws.rs.Path b/plugins/webpush/src/main/resources/META-INF/javax.ws.rs.Path new file mode 100644 index 000000000..4a2cc15e7 --- /dev/null +++ b/plugins/webpush/src/main/resources/META-INF/javax.ws.rs.Path @@ -0,0 +1,2 @@ +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.rest.WebPushSubscriptionRescource +de.unistuttgart.iaas.amyassist.amy.plugin.webpush.rest.WebPushNotificationRescource diff --git a/pom.xml b/pom.xml index fb83d8ea6..17ec21941 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ pom de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 Amy Personal Assistance System @@ -19,23 +19,24 @@ UTF-8 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 - 0.5.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 + 0.6.0 @@ -47,7 +48,7 @@ 2.27 2.1 1.7.25 - 4.4.9 + 4.4.10 2.0.2 3.7 110413 @@ -72,13 +73,19 @@ plugins/example plugins/spotify plugins/systemtime + plugins/timer plugins/weather plugins/navigation + plugins/social + plugins/webpush + plugins/tosca + plugins/mensa amy-message-hub-api amy-message-hub remote-sr deployment-descriptor-utility natlang-api + chat-socket natlang @@ -169,6 +176,11 @@ amy-natlang ${amy.natlang.version} + + de.unistuttgart.iaas.amyassist + amy-chat-socket + ${amy.chatsocket.version} + de.unistuttgart.iaas.amyassist amy-natlang-api @@ -214,6 +226,11 @@ httpcore ${httpcore.version} + + org.apache.httpcomponents + httpcore-nio + ${httpcore.version} + org.slf4j slf4j-api diff --git a/registry/pom.xml b/registry/pom.xml index 639a4f282..54e016670 100644 --- a/registry/pom.xml +++ b/registry/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/remote-sr/pom.xml b/remote-sr/pom.xml index b8d91a640..eae012c51 100644 --- a/remote-sr/pom.xml +++ b/remote-sr/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/rest-resources/pom.xml b/rest-resources/pom.xml index 29ff232df..9fc0912b7 100644 --- a/rest-resources/pom.xml +++ b/rest-resources/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/AnswerConsumer.java b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/AnswerConsumer.java index cf0e79191..ab753e15f 100644 --- a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/AnswerConsumer.java +++ b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/AnswerConsumer.java @@ -23,6 +23,8 @@ package de.unistuttgart.iaas.amyassist.amy.restresources.chat; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; + import java.util.Queue; @@ -32,7 +34,7 @@ */ class AnswerConsumer { - private Queue queue; + private Queue queue; /** * empty constructor @@ -47,7 +49,7 @@ public AnswerConsumer() { * add a new answer to the queue * @param s the answer */ - void addToQueue(String s){ + void addToQueue(Response s){ this.queue.add(s); } @@ -55,7 +57,7 @@ void addToQueue(String s){ * sets the internal queue * @param queue to set */ - public void setQueue(Queue queue) { + public void setQueue(Queue queue) { this.queue = queue; } diff --git a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatResource.java b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatResource.java index a322a189f..a887c09bd 100644 --- a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatResource.java +++ b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatResource.java @@ -36,6 +36,7 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Reference; import de.unistuttgart.iaas.amyassist.amy.core.natlang.DialogHandler; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.ExampleObject; @@ -116,17 +117,17 @@ public void useAmy(@QueryParam("langInput") String input, @QueryParam("clientUUI @Path("response") @Consumes(MediaType.TEXT_PLAIN) @Produces(MediaType.TEXT_PLAIN) - public String receiveResponse(String uuidString) { + public Response receiveResponse(String uuidString) { try { UUID uuid = UUID.fromString(uuidString); if (this.chatService.getQueue(uuid) != null) { - String s = this.chatService.getQueue(uuid).poll(); - if (s == null) { - return ""; + Response response = this.chatService.getQueue(uuid).poll(); + if (response == null) { + return Response.text("").build(); } - return s; + return response; } - return ""; + return Response.text("").build(); } catch (IllegalArgumentException e) { throw new WebApplicationException("unknown UUID " + uuidString, e, Status.FORBIDDEN); diff --git a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatService.java b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatService.java index e1366079a..4b1d6cb07 100644 --- a/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatService.java +++ b/rest-resources/src/main/java/de/unistuttgart/iaas/amyassist/amy/restresources/chat/ChatService.java @@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentMap; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.natlang.Response; /** * Helper service for the chat inside the webapp @@ -41,14 +42,14 @@ public class ChatService { /** * maps uuids from users to a LinkedList containing answers from amy */ - private ConcurrentMap> userQueueMap = new ConcurrentHashMap<>(); + private ConcurrentMap> userQueueMap = new ConcurrentHashMap<>(); /** * retrieve queue from user with uuid * @param uuid of user * @return the queue of answers from amy */ - public Queue getQueue(UUID uuid) { + public Queue getQueue(UUID uuid) { if(this.userQueueMap.containsKey(uuid)) { return this.userQueueMap.get(uuid); } @@ -61,7 +62,7 @@ public Queue getQueue(UUID uuid) { * @param uuid string representation of uuid */ public void addUser(UUID uuid) { - this.userQueueMap.put(uuid, new LinkedList()); + this.userQueueMap.put(uuid, new LinkedList()); } diff --git a/scripts/systemtest/systemtests.sh b/scripts/systemtest/systemtests.sh index 852d8990f..8b75939e4 100755 --- a/scripts/systemtest/systemtests.sh +++ b/scripts/systemtest/systemtests.sh @@ -1,26 +1,21 @@ #!/bin/bash +WAIT_TO_START=20 absPath=$(readlink -f "$0") absDir=$(dirname "$absPath") cd "${absDir}" docker-compose up -d -stop=false -remainingTries=20 -while [ "$stop" == "false" ] ;do - sleep 1 - curl -I -S --show-error localhost - curlCode=$? - if [ $curlCode -eq 0 ] || [ $remainingTries -eq 0 ] ;then stop=true ;fi - let "remainingTries=$remainingTries - 1" -done +sleep $WAIT_TO_START +curl -I -S --show-error localhost +curlCode=$? docker-compose stop docker-compose logs master-node contCode=$(docker inspect -f '{{.State.ExitCode}}' master-node) errorCount=$(docker-compose logs master-node | grep -i -E -c "(Error|Exception)") if [ $curlCode -ne 0 ] ;then echo "Curl:$curlCode" ;fi -if [ $contCode -ne 0 ] ;then echo "Container:$contCode" ;fi +if [ $contCode -ne 143 ] ;then echo "Container:$contCode" ;fi if [ $errorCount -ne 0 ] ;then echo "Error count:$errorCount" ;fi #Don't include container code, because Amy get's killed by docker-compose stop, which causes a code != 0 -if [ $curlCode -ne 0 ] || [ $errorCount -ne 0 ] ;then exit 1 ;fi +if [ $curlCode -ne 0 ] || [ $contCode -ne 143 ] || [ $errorCount -ne 0 ] ;then exit 1 ;fi diff --git a/testing-framework/pom.xml b/testing-framework/pom.xml index 8849a9140..37ec18775 100644 --- a/testing-framework/pom.xml +++ b/testing-framework/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/LoggerProvider.java b/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/LoggerProvider.java index 53fde9e08..d238f4503 100644 --- a/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/LoggerProvider.java +++ b/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/LoggerProvider.java @@ -32,14 +32,12 @@ import de.unistuttgart.iaas.amyassist.amy.core.di.ContextLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescription; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceDescriptionImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceImplementationDescription; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceInstantiationDescription; import de.unistuttgart.iaas.amyassist.amy.core.di.SimpleServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.consumer.ServiceConsumer; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandle; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceHandleImpl; -import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceImplementationDescriptionImpl; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.ServiceProvider; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceDescriptionImpl; +import de.unistuttgart.iaas.amyassist.amy.core.di.runtime.ServiceInstantiationDescriptionImpl; /** * Logger Service provider for tests @@ -56,23 +54,24 @@ public class LoggerProvider implements ServiceProvider { } @Override - public ServiceImplementationDescription getServiceImplementationDescription(@Nonnull ContextLocator locator, + public ServiceInstantiationDescription getServiceInstantiationDescription(@Nonnull ContextLocator locator, @Nonnull ServiceConsumer serviceConsumer) { Class cls = serviceConsumer.getConsumerClass(); - return new ServiceImplementationDescriptionImpl<>(serviceConsumer.getServiceDescription(), + return new ServiceInstantiationDescriptionImpl<>(serviceConsumer.getServiceDescription(), Collections.singletonMap(KEY, cls), LoggerFactory.class); } @Override - public @Nonnull ServiceHandle createService(@Nonnull SimpleServiceLocator locator, - @Nonnull ServiceImplementationDescription serviceImplementationDescription) { + public @Nonnull Logger createService(@Nonnull SimpleServiceLocator locator, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { - Class cls = (Class) serviceImplementationDescription.getContext().get(KEY); - return new ServiceHandleImpl<>(LoggerFactory.getLogger(cls)); + Class cls = (Class) serviceInstantiationDescription.getContext().get(KEY); + return LoggerFactory.getLogger(cls); } @Override - public void dispose(ServiceHandle service) { + public void dispose(@Nonnull Logger service, + @Nonnull ServiceInstantiationDescription serviceInstantiationDescription) { // nothing to do here } diff --git a/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/TestFrameworkImpl.java b/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/TestFrameworkImpl.java index 773e033bd..7e016290c 100644 --- a/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/TestFrameworkImpl.java +++ b/testing-framework/src/main/java/de/unistuttgart/iaas/amyassist/amy/test/TestFrameworkImpl.java @@ -37,9 +37,11 @@ import org.slf4j.bridge.SLF4JBridgeHandler; import de.unistuttgart.iaas.amyassist.amy.core.configuration.ConfigurationManager; +import de.unistuttgart.iaas.amyassist.amy.core.di.Configuration; import de.unistuttgart.iaas.amyassist.amy.core.di.DependencyInjection; -import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceNotFoundException; +import de.unistuttgart.iaas.amyassist.amy.core.di.ServiceLocator; import de.unistuttgart.iaas.amyassist.amy.core.di.annotation.Service; +import de.unistuttgart.iaas.amyassist.amy.core.di.exception.ServiceNotFoundException; import de.unistuttgart.iaas.amyassist.amy.core.di.provider.SingletonServiceProvider; import de.unistuttgart.iaas.amyassist.amy.core.di.util.Util; import de.unistuttgart.iaas.amyassist.amy.core.plugin.api.IStorage; @@ -67,6 +69,8 @@ public class TestFrameworkImpl implements TestFramework { private DependencyInjection dependencyInjection; private Server server; private List> restResources = new ArrayList<>(); + private Configuration configuration; + private ServiceLocator serviceLocator; /** * Create a new instance of the TestFramework @@ -76,10 +80,12 @@ public TestFrameworkImpl() { Mockito.withSettings().defaultAnswer(Mockito.CALLS_REAL_METHODS).useConstructor()); this.dependencyInjection = new DependencyInjection(); - this.dependencyInjection.register(new SingletonServiceProvider<>(TestFramework.class, this)); - this.dependencyInjection.register(new SingletonServiceProvider<>(IStorage.class, this.storage)); - this.dependencyInjection.register(ServerImpl.class); - this.dependencyInjection.register(new LoggerProvider()); + this.configuration = this.dependencyInjection.getConfiguration(); + this.serviceLocator = this.dependencyInjection.getServiceLocator(); + this.configuration.register(new SingletonServiceProvider<>(TestFramework.class, this)); + this.configuration.register(new SingletonServiceProvider<>(IStorage.class, this.storage)); + this.configuration.register(ServerImpl.class); + this.configuration.register(new LoggerProvider()); } /** @@ -94,7 +100,7 @@ public void prepareServer() { ConfigurationManager configLoader = this.mockService(ConfigurationManager.class); Mockito.when(configLoader.getConfigurationWithDefaults(ServerImpl.CONFIG_NAME)).thenReturn(serverConfig); - this.server = this.dependencyInjection.getService(Server.class); + this.server = this.serviceLocator.getService(Server.class); } /** @@ -103,7 +109,7 @@ public void prepareServer() { * the object of the test class */ public void setup(Object testInstance) { - this.dependencyInjection.inject(testInstance); + this.serviceLocator.inject(testInstance); } /** @@ -133,16 +139,16 @@ public IStorage storage() { @Override public T mockService(Class serviceType) { T mock = Mockito.mock(serviceType); - this.dependencyInjection.register(new SingletonServiceProvider<>(serviceType, mock)); + this.configuration.register(new SingletonServiceProvider<>(serviceType, mock)); return mock; } @Override public T setServiceUnderTest(Class serviceClass) { if (Util.isValidServiceClass(serviceClass) && serviceClass.isAnnotationPresent(Service.class)) { - this.dependencyInjection.register(serviceClass); + this.configuration.register(serviceClass); try { - return this.dependencyInjection.createAndInitialize(serviceClass); + return this.serviceLocator.createAndInitialize(serviceClass); } catch (ServiceNotFoundException e) { throw new IllegalStateException("The dependencies of " + serviceClass.getName() + " must be mocked with mockService before calling this method!", e); @@ -164,7 +170,7 @@ public WebTarget setRESTResource(Class resource) { @Override public T registerService(Class serviceType, Class serviceClass) { - this.dependencyInjection.register(serviceClass); - return this.dependencyInjection.getService(serviceType); + this.configuration.register(serviceClass); + return this.serviceLocator.getService(serviceType); } } diff --git a/utility/pom.xml b/utility/pom.xml index bf3164eba..a68bbfb40 100644 --- a/utility/pom.xml +++ b/utility/pom.xml @@ -8,7 +8,7 @@ de.unistuttgart.iaas.amyassist amy - 0.5.0 + 0.6.0 ../pom.xml diff --git a/utility/src/main/java/de/unistuttgart/iaas/amyassist/amy/utility/rest/adapter/DurationAdapter.java b/utility/src/main/java/de/unistuttgart/iaas/amyassist/amy/utility/rest/adapter/DurationAdapter.java new file mode 100644 index 000000000..175bc8ad0 --- /dev/null +++ b/utility/src/main/java/de/unistuttgart/iaas/amyassist/amy/utility/rest/adapter/DurationAdapter.java @@ -0,0 +1,53 @@ +/* + * This source file is part of the Amy open source project. + * For more information see github.com/AmyAssist + * + * Copyright (c) 2018 the Amy project authors. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * For more information see notice.md + */ + +package de.unistuttgart.iaas.amyassist.amy.utility.rest.adapter; + +import java.time.Duration; + +import javax.xml.bind.annotation.adapters.XmlAdapter; + +/** + * An adapter for LocalDateTime objects to parse and get Strings according to ISO-8601 + * + * @author Muhammed Kaya + */ +public class DurationAdapter extends XmlAdapter { + + /** + * @see javax.xml.bind.annotation.adapters.XmlAdapter#marshal(java.lang.Object) + */ + @Override + public String marshal(Duration v) { + return v.toString(); + } + + /** + * @see javax.xml.bind.annotation.adapters.XmlAdapter#unmarshal(java.lang.Object) + */ + @Override + public Duration unmarshal(String v) { + return Duration.parse(v); + } + +}