diff --git a/.classpath b/.classpath index 439fabc..1146f18 100644 --- a/.classpath +++ b/.classpath @@ -1,6 +1,5 @@ - @@ -32,6 +31,11 @@ - + + + + + + diff --git a/.gitignore b/.gitignore index fa4147a..b57af9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .DS_Store +.settings/ package-lock.json /out/ /bin/ diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 49aecb7..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,576 +0,0 @@ -eclipse.preferences.version=1 -enableParallelJavaIndexSearch=true -org.eclipse.jdt.core.builder.annotationPath.allLocations=disabled -org.eclipse.jdt.core.builder.cleanOutputFolder=clean -org.eclipse.jdt.core.builder.duplicateResourceTask=warning -org.eclipse.jdt.core.builder.invalidClasspath=abort -org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore -org.eclipse.jdt.core.builder.resourceCopyExclusionFilter= -org.eclipse.jdt.core.circularClasspath=warning -org.eclipse.jdt.core.classpath.exclusionPatterns=enabled -org.eclipse.jdt.core.classpath.mainOnlyProjectHasTestOnlyDependency=error -org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled -org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error -org.eclipse.jdt.core.codeComplete.argumentPrefixes= -org.eclipse.jdt.core.codeComplete.argumentSuffixes= -org.eclipse.jdt.core.codeComplete.camelCaseMatch=enabled -org.eclipse.jdt.core.codeComplete.deprecationCheck=disabled -org.eclipse.jdt.core.codeComplete.discouragedReferenceCheck=disabled -org.eclipse.jdt.core.codeComplete.fieldPrefixes= -org.eclipse.jdt.core.codeComplete.fieldSuffixes= -org.eclipse.jdt.core.codeComplete.forbiddenReferenceCheck=enabled -org.eclipse.jdt.core.codeComplete.forceImplicitQualification=disabled -org.eclipse.jdt.core.codeComplete.localPrefixes= -org.eclipse.jdt.core.codeComplete.localSuffixes= -org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= -org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= -org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= -org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= -org.eclipse.jdt.core.codeComplete.subwordMatch=disabled -org.eclipse.jdt.core.codeComplete.suggestStaticImports=enabled -org.eclipse.jdt.core.codeComplete.visibilityCheck=enabled -org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled -org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore -org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull -org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault -org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= -org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable -org.eclipse.jdt.core.compiler.annotation.nullable.secondary= -org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.lambda.genericSignature=do not generate -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.shareCommonFinallyBlocks=disabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=17 -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.emulateJavacBug8031744=enabled -org.eclipse.jdt.core.compiler.generateClassFiles=enabled -org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 -org.eclipse.jdt.core.compiler.problem.APILeak=warning -org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info -org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.autoboxing=ignore -org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning -org.eclipse.jdt.core.compiler.problem.deadCode=warning -org.eclipse.jdt.core.compiler.problem.deadCodeInTrivialIfStatement=disabled -org.eclipse.jdt.core.compiler.problem.deprecation=warning -org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled -org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled -org.eclipse.jdt.core.compiler.problem.discouragedReference=warning -org.eclipse.jdt.core.compiler.problem.emptyStatement=ignore -org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore -org.eclipse.jdt.core.compiler.problem.fallthroughCase=ignore -org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled -org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore -org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning -org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning -org.eclipse.jdt.core.compiler.problem.forbiddenReference=error -org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning -org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled -org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning -org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning -org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore -org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore -org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled -org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public -org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore -org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning -org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore -org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled -org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag -org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled -org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore -org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled -org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore -org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore -org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning -org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning -org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore -org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning -org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning -org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error -org.eclipse.jdt.core.compiler.problem.nullReference=warning -org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error -org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning -org.eclipse.jdt.core.compiler.problem.overridingMethodWithoutSuperInvocation=ignore -org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning -org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore -org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning -org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore -org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore -org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore -org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning -org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning -org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore -org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore -org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning -org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore -org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning -org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled -org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning -org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled -org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled -org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info -org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled -org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore -org.eclipse.jdt.core.compiler.problem.tasks=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=ignore -org.eclipse.jdt.core.compiler.problem.uninternedIdentityComparison=disabled -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning -org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled -org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info -org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore -org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore -org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore -org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled -org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedImport=warning -org.eclipse.jdt.core.compiler.problem.unusedLabel=warning -org.eclipse.jdt.core.compiler.problem.unusedLocal=warning -org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled -org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled -org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeArgumentsForMethodInvocation=warning -org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore -org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning -org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.processAnnotations=disabled -org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 -org.eclipse.jdt.core.compiler.storeAnnotations=disabled -org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled -org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL -org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX -org.eclipse.jdt.core.computeJavaBuildOrder=ignore -org.eclipse.jdt.core.encoding=utf8 -org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false -org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 -org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=true -org.eclipse.jdt.core.formatter.align_type_members_on_columns=false -org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false -org.eclipse.jdt.core.formatter.align_with_spaces=false -org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0 -org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 -org.eclipse.jdt.core.formatter.alignment_for_assertion_message=16 -org.eclipse.jdt.core.formatter.alignment_for_assignment=0 -org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 -org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 -org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 -org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=16 -org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=16 -org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 -org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 -org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 -org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 -org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_record_components=16 -org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 -org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 -org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 -org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 -org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 -org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 -org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=20 -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_annotations=0 -org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 -org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 -org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 -org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_after_package=1 -org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_field=0 -org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 -org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 -org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 -org.eclipse.jdt.core.formatter.blank_lines_before_method=1 -org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 -org.eclipse.jdt.core.formatter.blank_lines_before_package=0 -org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 -org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0 -org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 -org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=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_record_constructor=end_of_line -org.eclipse.jdt.core.formatter.brace_position_for_record_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.align_tags_descriptions_grouped=true -org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false -org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false -org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true -org.eclipse.jdt.core.formatter.comment.format_block_comments=true -org.eclipse.jdt.core.formatter.comment.format_header=false -org.eclipse.jdt.core.formatter.comment.format_html=true -org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true -org.eclipse.jdt.core.formatter.comment.format_line_comments=true -org.eclipse.jdt.core.formatter.comment.format_source_code=true -org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false -org.eclipse.jdt.core.formatter.comment.indent_root_tags=false -org.eclipse.jdt.core.formatter.comment.indent_tag_description=false -org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert -org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert -org.eclipse.jdt.core.formatter.comment.line_length=80 -org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true -org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true -org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false -org.eclipse.jdt.core.formatter.compact_else_if=true -org.eclipse.jdt.core.formatter.continuation_indentation=2 -org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 -org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off -org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on -org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false -org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true -org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true -org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_empty_lines=false -org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true -org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true -org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true -org.eclipse.jdt.core.formatter.indentation.size=4 -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert -org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert -org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=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_space_after_additive_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert -org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert -org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert -org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert -org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert -org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert -org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert -org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert -org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert -org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert -org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert -org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert -org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert -org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert -org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert -org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert -org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert -org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert -org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert -org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert -org.eclipse.jdt.core.formatter.join_lines_in_comments=false -org.eclipse.jdt.core.formatter.join_wrapped_lines=false -org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false -org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false -org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false -org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false -org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never -org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false -org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never -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_after_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0 -org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0 -org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 -org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines -org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines -org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true -org.eclipse.jdt.core.formatter.tabulation.char=space -org.eclipse.jdt.core.formatter.tabulation.size=4 -org.eclipse.jdt.core.formatter.text_block_indentation=0 -org.eclipse.jdt.core.formatter.use_on_off_tags=true -org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false -org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true -org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true -org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false -org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true -org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true -org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true -org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true -org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true -org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true -org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true -org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true -org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false -org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true -org.eclipse.jdt.core.incompatibleJDKLevel=ignore -org.eclipse.jdt.core.incompleteClasspath=error -org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter -org.eclipse.jdt.core.timeoutForParameterNameFromAttachedJavadoc=50 diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 55712c1..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "typescript.tsdk": "node_modules/typescript/lib" -} \ No newline at end of file diff --git a/build.xml b/build.xml index 78d6db4..dc18969 100644 --- a/build.xml +++ b/build.xml @@ -11,6 +11,7 @@ + diff --git a/html/licenses.html b/html/licenses.html index df3c69c..5057cfc 100644 --- a/html/licenses.html +++ b/html/licenses.html @@ -8,7 +8,7 @@ - +
@@ -33,6 +33,10 @@ + + + + diff --git a/jars/openxliff.jar b/jars/openxliff.jar index 981dceb..c6182ed 100644 Binary files a/jars/openxliff.jar and b/jars/openxliff.jar differ diff --git a/jars/sqlite-jdbc-3.43.0.0.jar b/jars/sqlite-jdbc-3.43.0.0.jar new file mode 100644 index 0000000..3db85a4 Binary files /dev/null and b/jars/sqlite-jdbc-3.43.0.0.jar differ diff --git a/jars/xmljava.jar b/jars/xmljava.jar index f1c5bcc..45cd5a6 100644 Binary files a/jars/xmljava.jar and b/jars/xmljava.jar differ diff --git a/package.json b/package.json index c2e2bf2..a040ce7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "swordfish", "productName": "Swordfish", - "version": "4.30.0", + "version": "5.0.0", "description": "Swordfish Translation Editor", "main": "js/Swordfish.js", "scripts": { @@ -20,11 +20,11 @@ "url": "https://github.com/rmraya/Swordfish.git" }, "devDependencies": { - "electron": "^28.2.0", + "electron": "^29.0.1", "typescript": "^5.3.3" }, "dependencies": { - "mtengines": "^1.3.0", + "mtengines": "^1.3.2", "typesbcp47": "^1.2.0", "typesxml": "^1.2.1" } diff --git a/src/com/maxprograms/swordfish/Constants.java b/src/com/maxprograms/swordfish/Constants.java index f1ee9e6..afb2279 100644 --- a/src/com/maxprograms/swordfish/Constants.java +++ b/src/com/maxprograms/swordfish/Constants.java @@ -19,8 +19,8 @@ private Constants() { } public static final String APPNAME = "Swordfish"; - public static final String VERSION = "4.30.0"; - public static final String BUILD = "20240126_0948"; + public static final String VERSION = "5.0.0"; + public static final String BUILD = "20240224_1556"; public static final String REASON = "reason"; public static final String STATUS = "status"; diff --git a/src/com/maxprograms/swordfish/DbUpgrade.java b/src/com/maxprograms/swordfish/DbUpgrade.java new file mode 100644 index 0000000..42203c3 --- /dev/null +++ b/src/com/maxprograms/swordfish/DbUpgrade.java @@ -0,0 +1,312 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +package com.maxprograms.swordfish; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.lang.System.Logger; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.text.MessageFormat; + +public class DbUpgrade { + + static Logger logger = System.getLogger(DbUpgrade.class.getName()); + + private DbUpgrade() { + // private constructor to prevent instantiation + } + + public static void main(String[] args) { + boolean upgradeMemory = false; + boolean upgradeProject = false; + String folder = ""; + for (String arg : args) { + if (arg.equals("-memory")) { + upgradeMemory = true; + continue; + } + if (arg.equals("-project")) { + upgradeProject = true; + continue; + } + if (!folder.isEmpty()) { + folder += " "; + } + folder += arg; + } + if (folder.isEmpty()) { + logger.log(Logger.Level.ERROR, Messages.getString("DbUpgrade.0")); + return; + } + if (upgradeProject) { + try { + upgradeProject(new File(folder)); + } catch (IOException | SQLException e) { + logger.log(Logger.Level.ERROR, e); + } + } + if (upgradeMemory) { + try { + upgradeMemory(new File(folder)); + } catch (IOException | SQLException e) { + logger.log(Logger.Level.ERROR, e); + } + } + } + + public static void upgradeProject(File projectFolder) throws IOException, SQLException { + MessageFormat mf = new MessageFormat(Messages.getString("DbUpgrade.1")); + logger.log(Logger.Level.INFO, mf.format(new String[] { projectFolder.getName() })); + File sqliteFolder = new File(projectFolder, "sqlite"); + if (!sqliteFolder.exists()) { + Files.createDirectories(sqliteFolder.toPath()); + } + File newDB = new File(sqliteFolder, "database.db"); + File h2Folder = new File(projectFolder, "h2data"); + try (Connection newConn = DriverManager + .getConnection("jdbc:sqlite:" + newDB.getAbsolutePath().replace('\\', '/'))) { + newConn.setAutoCommit(false); + createTables(newConn); + String url = "jdbc:h2:" + h2Folder.getAbsolutePath() + "/db"; + try (Connection oldConn = DriverManager.getConnection(url)) { + migrateFiles(oldConn, newConn); + migrateSegments(oldConn, newConn); + } + } + File backup = new File(projectFolder, "h2db"); + Files.move(h2Folder.toPath(), backup.toPath()); + } + + private static void migrateSegments(Connection oldConn, Connection newConn) throws SQLException, IOException { + String selectSql = "SELECT file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words, idx FROM segments"; + String insertSql = "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words, idx) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + int count = 0; + try (PreparedStatement newPstmt = newConn.prepareStatement(insertSql)) { + try (Statement stmt = oldConn.createStatement()) { + try (ResultSet rs = stmt.executeQuery(selectSql)) { + while (rs.next()) { + String file = rs.getString(1); + String unitId = rs.getString(2); + String segId = rs.getString(3); + String type = rs.getString(4); + String state = rs.getString(5); + int child = rs.getInt(6); + String translate = rs.getString(7); + int tags = rs.getInt(8); + String space = rs.getString(9); + String source = getString(rs.getNCharacterStream(10)); + String sourceText = getString(rs.getNCharacterStream(11)); + String target = getString(rs.getNCharacterStream(12)); + String targetText = getString(rs.getNCharacterStream(13)); + int words = rs.getInt(14); + int idx = rs.getInt(15); + newPstmt.setString(1, file); + newPstmt.setString(2, unitId); + newPstmt.setString(3, segId); + newPstmt.setString(4, type); + newPstmt.setString(5, state); + newPstmt.setInt(6, child); + newPstmt.setString(7, translate); + newPstmt.setInt(8, tags); + newPstmt.setString(9, space); + newPstmt.setString(10, source); + newPstmt.setString(11, sourceText); + newPstmt.setString(12, target); + newPstmt.setString(13, targetText); + newPstmt.setInt(14, words); + newPstmt.setInt(15, idx); + newPstmt.execute(); + if (count++ % 1000 == 0) { + newConn.commit(); + } + } + } + } + } + newConn.commit(); + } + + private static void migrateFiles(Connection oldConn, Connection newConn) throws SQLException, IOException { + String selectSql = "SELECT id, name FROM files"; + String insertSql = "INSERT INTO files (id, name) VALUES (?,?)"; + try (PreparedStatement newPstmt = newConn.prepareStatement(insertSql)) { + try (Statement stmt = oldConn.createStatement()) { + try (ResultSet rs = stmt.executeQuery(selectSql)) { + while (rs.next()) { + String id = rs.getString(1); + String name = getString(rs.getNCharacterStream(2)); + newPstmt.setString(1, id); + newPstmt.setString(2, name); + newPstmt.execute(); + } + } + } + } + newConn.commit(); + } + + private static void createTables(Connection newConn) throws SQLException { + String files = """ + CREATE TABLE files ( + id VARCHAR(50) NOT NULL, + name VARCHAR(350) NOT NULL, + PRIMARY KEY(id, name) + );"""; + String units = """ + CREATE TABLE units ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + data TEXT NOT NULL, + compressed CHAR(1) NOT NULL DEFAULT 'N', + PRIMARY KEY(file, unitId) + );"""; + String segments = """ + CREATE TABLE segments ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + type CHAR(1) NOT NULL DEFAULT 'S', + state VARCHAR(12) DEFAULT 'initial', + child INTEGER, + translate CHAR(1), + tags INTEGER DEFAULT 0, + space CHAR(1) DEFAULT 'N', + source TEXT NOT NULL, + sourceText TEXT NOT NULL, + target TEXT NOT NULL, + targetText TEXT NOT NULL, + words INTEGER NOT NULL DEFAULT 0, + idx INTEGER, + PRIMARY KEY(file, unitId, segId, type) + );"""; + String matches = """ + CREATE TABLE matches ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + matchId varchar(256), + origin VARCHAR(256), + type CHAR(2) NOT NULL DEFAULT 'tm', + similarity INTEGER DEFAULT 0, + source TEXT NOT NULL, + target TEXT NOT NULL, + data TEXT NOT NULL, + compressed CHAR(1) NOT NULL DEFAULT 'N', + PRIMARY KEY(file, unitId, segId, matchid) + );"""; + String terms = """ + CREATE TABLE terms ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + termid varchar(256), + origin VARCHAR(256), + source TEXT NOT NULL, + target TEXT NOT NULL, + PRIMARY KEY(file, unitId, segId, termid) + );"""; + String notes = """ + CREATE TABLE notes ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + noteid varchar(256) NOT NULL, + note TEXT NOT NULL, + PRIMARY KEY(file, unitId, segId, noteid) + );"""; + try (Statement create = newConn.createStatement()) { + create.execute(files); + create.execute(units); + create.execute(segments); + create.execute(matches); + create.execute(terms); + create.execute(notes); + } + newConn.commit(); + } + + public static void upgradeMemory(File databaseFolder) throws SQLException, IOException { + MessageFormat mf = new MessageFormat(Messages.getString("DbUpgrade.2")); + logger.log(Logger.Level.INFO, mf.format(new String[] { databaseFolder.getName() })); + DriverManager.registerDriver(new org.sqlite.JDBC()); + File newDB = new File(databaseFolder, "database.db"); + try (Connection newConn = DriverManager + .getConnection("jdbc:sqlite:" + newDB.getAbsolutePath().replace('\\', '/'))) { + newConn.setAutoCommit(false); + String sql = """ + CREATE TABLE tuv ( + tuid VARCHAR(256) NOT NULL, + lang VARCHAR(15) NOT NULL, + seg TEXT NOT NULL, + puretext TEXT NOT NULL, + textlength INTEGER NOT NULL, + PRIMARY KEY(tuid, lang) + );"""; + try (Statement stmt = newConn.createStatement()) { + stmt.execute(sql); + } + newConn.commit(); + + String url = "jdbc:h2:" + databaseFolder.getAbsolutePath() + "/db"; + try (Connection oldConn = DriverManager.getConnection(url)) { + String selectSql = "SELECT tuid, lang, seg, puretext, textlength FROM tuv"; + String insertSql = "INSERT INTO tuv (tuid, lang, seg, puretext, textlength) VALUES (?,?,?,?,?)"; + int count = 0; + try (PreparedStatement newPstmt = newConn.prepareStatement(insertSql)) { + try (Statement stmt = oldConn.createStatement()) { + try (ResultSet rs = stmt.executeQuery(selectSql)) { + while (rs.next()) { + String tuid = rs.getString(1); + String lang = rs.getString(2); + String seg = getString(rs.getNCharacterStream(3)); + String puretext = getString(rs.getNCharacterStream(4)); + int textlength = rs.getInt(5); + newPstmt.setString(1, tuid); + newPstmt.setString(2, lang); + newPstmt.setString(3, seg); + newPstmt.setString(4, puretext); + newPstmt.setInt(5, textlength); + newPstmt.execute(); + if (count++ % 1000 == 0) { + newConn.commit(); + } + } + } + } + } + } + newConn.commit(); + } + File oldDb = new File(databaseFolder, "db.mv.db"); + File backup = new File(databaseFolder, "h2db.db"); + Files.move(oldDb.toPath(), backup.toPath()); + } + + public static String getString(Reader reader) throws IOException { + StringBuilder sb = new StringBuilder(); + char[] array = new char[1024]; + int read = 0; + while ((read = reader.read(array)) != -1) { + sb.append(array, 0, read); + } + reader.close(); + return sb.toString(); + } +} diff --git a/src/com/maxprograms/swordfish/GlossariesHandler.java b/src/com/maxprograms/swordfish/GlossariesHandler.java index c9b810c..01f270f 100644 --- a/src/com/maxprograms/swordfish/GlossariesHandler.java +++ b/src/com/maxprograms/swordfish/GlossariesHandler.java @@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.sql.SQLException; +import java.text.MessageFormat; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; @@ -47,8 +48,8 @@ import com.maxprograms.swordfish.models.Memory; import com.maxprograms.swordfish.tbx.Tbx2Tmx; import com.maxprograms.swordfish.tm.ITmEngine; -import com.maxprograms.swordfish.tm.InternalDatabase; import com.maxprograms.swordfish.tm.RemoteDatabase; +import com.maxprograms.swordfish.tm.SqliteDatabase; import com.maxprograms.xml.Element; import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpHandler; @@ -59,7 +60,7 @@ public class GlossariesHandler implements HttpHandler { private static Map glossaries; private static Map engines; - private static Map openTasks; + private static Map openTasks = new Hashtable<>(); @Override public void handle(HttpExchange exchange) throws IOException { @@ -83,7 +84,8 @@ public void handle(HttpExchange exchange) throws IOException { } } } catch (IOException e) { - logger.log(Level.ERROR, "Error processing glossary " + exchange.getRequestURI().toString(), e); + MessageFormat mf = new MessageFormat(Messages.getString("GlossariesHandler.0")); + logger.log(Level.ERROR, mf.format(new String[] { exchange.getRequestURI().toString() }), e); } } @@ -111,7 +113,8 @@ private JSONObject processRequest(String url, String request) { } else if ("/glossaries/addTerm".equals(url)) { response = addTerm(request); } else { - response.put(Constants.REASON, "Unknown request"); + MessageFormat mf = new MessageFormat(Messages.getString("GlossariesHandler.1")); + response.put(Constants.REASON, mf.format(new String[] { url })); } if (!response.has(Constants.REASON)) { @@ -131,22 +134,20 @@ private static JSONObject getProcessStatus(String request) { JSONObject json = new JSONObject(request); if (!json.has("process")) { JSONObject error = new JSONObject(); - error.put(Constants.REASON, "Missing 'process' parameter"); + error.put(Constants.REASON, Messages.getString("GlossariesHandler.2")); return error; } String process = json.getString("process"); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } if (openTasks.containsKey(process)) { return openTasks.get(process); } JSONObject error = new JSONObject(); - error.put(Constants.REASON, "No such process: " + process); + MessageFormat mf = new MessageFormat(Messages.getString("GlossariesHandler.3")); + error.put(Constants.REASON, mf.format(new String[] { process })); return error; } - private static JSONObject createGlossary(String request) throws IOException, SQLException { + private static JSONObject createGlossary(String request) throws IOException, SQLException, URISyntaxException { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("id")) { @@ -156,7 +157,7 @@ private static JSONObject createGlossary(String request) throws IOException, SQL json.put("creationDate", System.currentTimeMillis()); } Memory mem = new Memory(json); - InternalDatabase engine = new InternalDatabase(mem.getId(), getWorkFolder()); + ITmEngine engine = new SqliteDatabase(mem.getId(), getWorkFolder()); engine.close(); if (glossaries == null) { loadGlossariesList(); @@ -224,13 +225,10 @@ private static JSONObject deleteGlossary(String request) { if (json.has("glossaries")) { final String process = "" + System.currentTimeMillis(); result.put("process", process); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { JSONArray array = json.getJSONArray("glossaries"); for (int i = 0; i < array.length(); i++) { @@ -251,9 +249,9 @@ private static JSONObject deleteGlossary(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); } else { - result.put(Constants.REASON, "Missing 'glossaries' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.4")); } return result; } @@ -263,7 +261,8 @@ private static void deleteGlossaryFolder(String id) { File wfolder = new File(getWorkFolder(), id); TmsServer.deleteFolder(wfolder); } catch (IOException ioe) { - logger.log(Level.WARNING, "Folder '" + id + "' will be deleted on next start"); + MessageFormat mf = new MessageFormat(Messages.getString("GlossariesHandler.5")); + logger.log(Level.WARNING, mf.format(new String[] { id })); } } @@ -271,24 +270,21 @@ private static JSONObject exportGlossary(String request) { JSONObject result = new JSONObject(); final JSONObject json = new JSONObject(request); if (!json.has("glossary")) { - result.put(Constants.REASON, "Missing 'glossary' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.6")); return result; } if (!json.has("file")) { - result.put(Constants.REASON, "Missing 'file' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.7")); return result; } if (!json.has("srcLang")) { json.put("srcLang", "*all*"); } final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { if (glossaries == null) { loadGlossariesList(); @@ -311,13 +307,14 @@ private static JSONObject exportGlossary(String request) { JSONObject completed = new JSONObject(); completed.put(Constants.PROGRESS, Constants.COMPLETED); openTasks.put(process, completed); - } catch (IOException | JSONException | SAXException | ParserConfigurationException | SQLException | URISyntaxException e) { + } catch (IOException | JSONException | SAXException | ParserConfigurationException | SQLException + | URISyntaxException e) { logger.log(Level.ERROR, e.getMessage(), e); JSONObject error = new JSONObject(); error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -333,7 +330,7 @@ public static synchronized void openGlossary(Memory memory) throws IOException, if (glossaries == null) { loadGlossariesList(); } - ITmEngine engine = memory.getType().equals(Memory.LOCAL) ? new InternalDatabase(memory.getId(), getWorkFolder()) + ITmEngine engine = memory.getType().equals(Memory.LOCAL) ? new SqliteDatabase(memory.getId(), getWorkFolder()) : new RemoteDatabase(memory.getServer(), memory.getUser(), memory.getPassword(), memory.getId()); engines.put(memory.getId(), engine); } @@ -364,7 +361,7 @@ public static synchronized void closeAll() throws IOException, SQLException, URI } engines.clear(); if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Glossaries closed"); + logger.log(Level.INFO, Messages.getString("GlossariesHandler.8")); } } @@ -372,29 +369,25 @@ private JSONObject importGlossary(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("glossary")) { - result.put(Constants.REASON, "Missing 'glossary' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.9")); return result; } String id = json.getString("glossary"); if (!json.has("file")) { - result.put(Constants.REASON, "Missing 'file' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.10")); return result; } File glossFile = new File(json.getString("file")); if (!glossFile.exists()) { - result.put(Constants.REASON, "Glossary file does not exist"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.11")); return result; } - final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { openGlossary(glossaries.get(id)); File tempFile = null; @@ -410,7 +403,8 @@ private JSONObject importGlossary(String request) { String subject = json.has("subject") ? json.getString("subject") : ""; try { int imported = engine.storeTMX(tmxFile, project, client, subject); - logger.log(Level.INFO, "Imported " + imported); + MessageFormat mf = new MessageFormat(Messages.getString("GlossariesHandler.12")); + logger.log(Level.INFO, mf.format(new String[] { "" + imported })); JSONObject completed = new JSONObject(); completed.put("imported", imported); completed.put(Constants.PROGRESS, Constants.COMPLETED); @@ -431,7 +425,7 @@ private JSONObject importGlossary(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -467,7 +461,7 @@ private boolean isTBX(File file) throws IOException { byte[] array = new byte[40960]; try (FileInputStream input = new FileInputStream(file)) { if (input.read(array) == -1) { - throw new IOException("Premature end of file"); + throw new IOException(Messages.getString("GlossariesHandler.13")); } } String string = ""; @@ -493,7 +487,7 @@ private JSONObject addTerm(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("glossary")) { - result.put(Constants.REASON, "Missing 'glossary' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.14")); return result; } try { @@ -528,7 +522,7 @@ public static JSONObject searchTerm(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("glossary")) { - result.put(Constants.REASON, "Missing 'glossary' parameter"); + result.put(Constants.REASON, Messages.getString("GlossariesHandler.14")); return result; } String searchStr = json.getString("searchStr"); diff --git a/src/com/maxprograms/swordfish/MemoriesHandler.java b/src/com/maxprograms/swordfish/MemoriesHandler.java index 788ed1e..f54a871 100644 --- a/src/com/maxprograms/swordfish/MemoriesHandler.java +++ b/src/com/maxprograms/swordfish/MemoriesHandler.java @@ -24,6 +24,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.sql.SQLException; +import java.text.MessageFormat; import java.util.Collections; import java.util.Hashtable; import java.util.Iterator; @@ -47,8 +48,8 @@ import com.maxprograms.languages.LanguageUtils; import com.maxprograms.swordfish.models.Memory; import com.maxprograms.swordfish.tm.ITmEngine; -import com.maxprograms.swordfish.tm.InternalDatabase; import com.maxprograms.swordfish.tm.RemoteDatabase; +import com.maxprograms.swordfish.tm.SqliteDatabase; import com.maxprograms.swordfish.xliff.XliffUtils; import com.maxprograms.xml.Element; import com.maxprograms.xml.TextNode; @@ -62,7 +63,8 @@ public class MemoriesHandler implements HttpHandler { private static Map memories; private static Map engines; - private static Map openTasks; + private static Map openTasks = new Hashtable<>(); + private static Map localEngines = new Hashtable<>(); @Override public void handle(HttpExchange exchange) { @@ -86,7 +88,8 @@ public void handle(HttpExchange exchange) { } } } catch (IOException e) { - logger.log(Level.ERROR, "Error processing memory " + exchange.getRequestURI().toString(), e); + MessageFormat mf = new MessageFormat(Messages.getString("MemoriesHandler.0")); + logger.log(Level.ERROR, mf.format(new String[] { exchange.getRequestURI().toString() }), e); } } @@ -113,9 +116,9 @@ private JSONObject processRequest(String url, String request) { } else if ("/memories/getLanguages".equals(url)) { response = getLanguages(request); } else { - response.put(Constants.REASON, "Unknown request"); + MessageFormat mf = new MessageFormat(Messages.getString("MemoriesHandler.1")); + response.put(Constants.REASON, mf.format(new String[] { url })); } - if (!response.has(Constants.REASON)) { response.put(Constants.STATUS, Constants.SUCCESS); } else { @@ -133,17 +136,14 @@ private static JSONObject getLanguages(String request) { JSONObject result = new JSONObject(); final JSONObject json = new JSONObject(request); if (!json.has("memory")) { - result.put(Constants.REASON, "Missing 'memory' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.2")); return result; } final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { if (memories == null) { loadMemoriesList(); @@ -165,7 +165,7 @@ private static JSONObject getLanguages(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -174,18 +174,21 @@ private static JSONObject getProcessStatus(String request) { JSONObject json = new JSONObject(request); if (!json.has("process")) { JSONObject error = new JSONObject(); - error.put(Constants.REASON, "Missing 'process' parameter"); + error.put(Constants.REASON, Messages.getString("MemoriesHandler.3")); return error; } String process = json.getString("process"); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } if (openTasks.containsKey(process)) { - return openTasks.get(process); + JSONObject status = openTasks.get(process); + if (localEngines.containsKey(process)) { + SqliteDatabase engine = localEngines.get(process); + status.put("imported", engine.getCount()); + } + return status; } JSONObject error = new JSONObject(); - error.put(Constants.REASON, "No such process: " + process); + MessageFormat mf = new MessageFormat(Messages.getString("MemoriesHandler.4")); + error.put(Constants.REASON, mf.format(new String[] { process })); return error; } @@ -193,7 +196,7 @@ private JSONObject concordanceSearch(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("memories")) { - result.put(Constants.REASON, "Missing 'memories' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.5")); return result; } String searchStr = json.getString("searchStr"); @@ -203,19 +206,16 @@ private JSONObject concordanceSearch(String request) { int limit = json.getInt("limit"); JSONArray memoriesArray = json.getJSONArray("memories"); final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { if (isRegexp) { try { Pattern.compile(searchStr); } catch (PatternSyntaxException e) { - throw new IOException("Invalid regular expression"); + throw new IOException(Messages.getString("MemoriesHandler.6")); } } List matches = new Vector<>(); @@ -236,7 +236,7 @@ private JSONObject concordanceSearch(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -245,38 +245,39 @@ private static JSONObject importTMX(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("memory")) { - result.put(Constants.REASON, "Missing 'memory' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.7")); return result; } String memory = json.getString("memory"); if (!json.has("tmx")) { - result.put(Constants.REASON, "Missing 'tmx' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.8")); return result; } File tmx = new File(json.getString("tmx")); if (!tmx.exists()) { - result.put(Constants.REASON, "TMX file does not exist"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.9")); return result; } final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { open(memory); ITmEngine engine = getEngine(memory); + if (engine.getType().equals(SqliteDatabase.class.getName())) { + localEngines.put(process, (SqliteDatabase) engine); + } String project = json.has("project") ? json.getString("project") : ""; String client = json.has("client") ? json.getString("client") : ""; String subject = json.has("subject") ? json.getString("subject") : ""; try { int imported = engine.storeTMX(tmx.getAbsolutePath(), project, client, subject); if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Imported " + imported); + MessageFormat mf = new MessageFormat(Messages.getString("MemoriesHandler.10")); + logger.log(Level.INFO, mf.format(new String[] { "" + imported })); } JSONObject completed = new JSONObject(); completed.put("imported", imported); @@ -288,6 +289,9 @@ private static JSONObject importTMX(String request) { openTasks.put(process, error); logger.log(Level.ERROR, e.getMessage(), e); } + if (engine.getType().equals(SqliteDatabase.class.getName())) { + localEngines.remove(process); + } close(memory); } catch (IOException | SQLException | URISyntaxException e) { logger.log(Level.ERROR, e.getMessage(), e); @@ -295,7 +299,7 @@ private static JSONObject importTMX(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -304,24 +308,21 @@ private static JSONObject exportMemory(String request) { JSONObject result = new JSONObject(); final JSONObject json = new JSONObject(request); if (!json.has("memory")) { - result.put(Constants.REASON, "Missing 'memory' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.11")); return result; } if (!json.has("tmx")) { - result.put(Constants.REASON, "Missing 'tmx' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.12")); return result; } if (!json.has("srcLang")) { json.put("srcLang", "*all*"); } final String process = "" + System.currentTimeMillis(); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { if (memories == null) { loadMemoriesList(); @@ -344,13 +345,14 @@ private static JSONObject exportMemory(String request) { JSONObject completed = new JSONObject(); completed.put(Constants.PROGRESS, Constants.COMPLETED); openTasks.put(process, completed); - } catch (IOException | SAXException | ParserConfigurationException | SQLException | JSONException | URISyntaxException e) { + } catch (IOException | SAXException | ParserConfigurationException | SQLException | JSONException + | URISyntaxException e) { logger.log(Level.ERROR, e.getMessage(), e); JSONObject error = new JSONObject(); error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); result.put("process", process); return result; } @@ -362,13 +364,10 @@ private static JSONObject deleteMemory(String request) { if (json.has("memories")) { final String process = "" + System.currentTimeMillis(); result.put("process", process); - if (openTasks == null) { - openTasks = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); openTasks.put(process, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { JSONArray array = json.getJSONArray("memories"); for (int i = 0; i < array.length(); i++) { @@ -390,9 +389,9 @@ private static JSONObject deleteMemory(String request) { error.put(Constants.REASON, e.getMessage()); openTasks.put(process, error); } - }).start(); + }); } else { - result.put(Constants.REASON, "Missing 'memories' parameter"); + result.put(Constants.REASON, Messages.getString("MemoriesHandler.13")); } return result; } @@ -402,7 +401,8 @@ private static void deleteMemoryFolder(String id) { File wfolder = new File(getWorkFolder(), id); TmsServer.deleteFolder(wfolder); } catch (IOException ioe) { - logger.log(Level.WARNING, "Folder '" + id + "' will be deleted on next start"); + MessageFormat mf = new MessageFormat(Messages.getString("MemoriesHandler.14")); + logger.log(Level.WARNING, mf.format(new String[] { id })); } } @@ -441,7 +441,7 @@ public static JSONArray getMemories() throws IOException { return result; } - private static JSONObject createMemory(String request) throws IOException, SQLException { + private static JSONObject createMemory(String request) throws IOException, SQLException, URISyntaxException { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!json.has("id")) { @@ -451,7 +451,7 @@ private static JSONObject createMemory(String request) throws IOException, SQLEx json.put("creationDate", System.currentTimeMillis()); } Memory mem = new Memory(json); - InternalDatabase engine = new InternalDatabase(mem.getId(), getWorkFolder()); + ITmEngine engine = new SqliteDatabase(mem.getId(), getWorkFolder()); engine.close(); if (memories == null) { loadMemoriesList(); @@ -510,7 +510,7 @@ public static synchronized void open(String id) throws IOException, SQLException } if (!engines.containsKey(id)) { Memory memory = memories.get(id); - ITmEngine engine = memory.getType().equals(Memory.LOCAL) ? new InternalDatabase(id, getWorkFolder()) + ITmEngine engine = memory.getType().equals(Memory.LOCAL) ? new SqliteDatabase(id, getWorkFolder()) : new RemoteDatabase(memory.getServer(), memory.getUser(), memory.getPassword(), id); engines.put(id, engine); } @@ -531,7 +531,7 @@ public static synchronized void closeAll() throws IOException, SQLException, URI } engines.clear(); if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Memories closed"); + logger.log(Level.INFO, Messages.getString("MemoriesHandler.15")); } } diff --git a/src/com/maxprograms/swordfish/Messages.java b/src/com/maxprograms/swordfish/Messages.java new file mode 100644 index 0000000..ed4974a --- /dev/null +++ b/src/com/maxprograms/swordfish/Messages.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +package com.maxprograms.swordfish; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Properties; + +public class Messages { + + private static Properties props; + + private Messages() { + // do not instantiate this class + } + + public static String getString(String key) { + String resourceName = "swordfish"; + try { + if (props == null) { + Locale locale = Locale.getDefault(); + String language = locale.getLanguage(); + String extension = "_" + language + ".properties"; + // check if there is a resource for full language code + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, check if there is a resource for language only + extension = "_" + language.substring(0, 2) + ".properties"; + } + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, use the default resource + extension = ".properties"; + } + try (InputStream is = Messages.class.getResourceAsStream(resourceName + extension)) { + try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + props = new Properties(); + props.load(reader); + } + } + } + return props.getProperty(key, '!' + key + '!'); + } catch (IOException | NullPointerException e) { + return '!' + key + '!'; + } + } +} diff --git a/src/com/maxprograms/swordfish/ProjectsHandler.java b/src/com/maxprograms/swordfish/ProjectsHandler.java index cd455ac..f819080 100644 --- a/src/com/maxprograms/swordfish/ProjectsHandler.java +++ b/src/com/maxprograms/swordfish/ProjectsHandler.java @@ -25,6 +25,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.sql.SQLException; +import java.text.MessageFormat; import java.time.LocalDate; import java.util.ArrayList; import java.util.Collections; @@ -64,8 +65,8 @@ public class ProjectsHandler implements HttpHandler { private static Logger logger = System.getLogger(ProjectsHandler.class.getName()); - private static Map projects; - private static Map processes; + private static Map projects = new Hashtable<>(); + private static Map processes = new Hashtable<>(); protected JSONObject projectsList; private String srxFile; @@ -82,9 +83,7 @@ public void handle(HttpExchange exchange) throws IOException { try (InputStream is = exchange.getRequestBody()) { request = TmsServer.readRequestBody(is); } - if (projects == null) { - loadProjectsList(); - } + loadProjectsList(); JSONObject response = processRequest(uri.toString(), request); byte[] bytes = response.toString().getBytes(StandardCharsets.UTF_8); exchange.sendResponseHeaders(200, bytes.length); @@ -99,7 +98,8 @@ public void handle(HttpExchange exchange) throws IOException { } } } catch (IOException | JSONException | SAXException | ParserConfigurationException e) { - logger.log(Level.ERROR, "Error processing projects " + exchange.getRequestURI().toString(), e); + MessageFormat mf = new MessageFormat(Messages.getString("ProjectsHandler.0")); + logger.log(Level.ERROR, mf.format(new String[] { exchange.getRequestURI().toString() }), e); } } @@ -216,7 +216,8 @@ private JSONObject processRequest(String url, String request) { } else if ("/projects/removeNote".equals(url)) { response = removeNote(request); } else { - response.put(Constants.REASON, "Unknown request " + url); + MessageFormat mf = new MessageFormat(Messages.getString("ProjectsHandler.1")); + response.put(Constants.REASON, mf.format(new String[] { url })); } if (!response.has(Constants.REASON)) { @@ -238,7 +239,7 @@ private JSONObject setMTMatches(String request) JSONObject json = new JSONObject(request); String project = json.getString("project"); if (!projects.containsKey(project)) { - result.put(Constants.REASON, "Project does not exist"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.2")); return result; } projectStores.get(project).setMTMatches(json); @@ -249,7 +250,7 @@ private JSONObject getProject(String request) throws IOException, JSONException JSONObject json = new JSONObject(request); if (!projects.containsKey(json.getString("project"))) { JSONObject result = new JSONObject(); - result.put(Constants.REASON, "Project does not exist"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.2")); return result; } return projects.get(json.getString("project")).toJSON(); @@ -259,7 +260,7 @@ private JSONObject getProjectFiles(String request) { JSONObject result = new JSONObject(); JSONObject json = new JSONObject(request); if (!projects.containsKey(json.getString("project"))) { - result.put(Constants.REASON, "Project does not exist"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.2")); return result; } Project project = projects.get(json.getString("project")); @@ -275,9 +276,6 @@ private JSONObject getProjectFiles(String request) { private JSONObject getProcessStatus(String request) { JSONObject json = new JSONObject(request); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject result = processes.get(json.getString("process")); if (result == null) { result = new JSONObject(); @@ -299,21 +297,18 @@ private JSONObject exportTranslations(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } } String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); try { - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { projectStores.get(project).exportTranslations(output); obj.put(Constants.PROGRESS, Constants.COMPLETED); @@ -324,9 +319,9 @@ private JSONObject exportTranslations(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (Exception e) { - logger.log(Level.ERROR, "Error exporting translations", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.4"), e); result.put(Constants.REASON, e.getMessage()); } return result; @@ -344,21 +339,18 @@ private JSONObject export(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.5"), e); result.put(Constants.REASON, e.getMessage()); return result; } } String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); try { - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { projectStores.get(project).exportXliff(output); obj.put(Constants.PROGRESS, Constants.COMPLETED); @@ -369,9 +361,9 @@ private JSONObject export(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (Exception e) { - logger.log(Level.ERROR, "Error exporting translations", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.4"), e); result.put(Constants.REASON, e.getMessage()); } return result; @@ -389,21 +381,18 @@ private JSONObject exportTMX(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } } String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); try { - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { Project prj = projects.get(project); projectStores.get(project).exportTMX(output, prj.getDescription(), prj.getClient(), @@ -416,10 +405,10 @@ private JSONObject exportTMX(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (Exception e) { - logger.log(Level.ERROR, "Error exporting TMX", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.6"), e); result.put(Constants.REASON, e.getMessage()); } return result; @@ -472,7 +461,7 @@ private JSONObject listProjects() throws IOException, JSONException, SAXExceptio } private void loadProjectsList() throws IOException, JSONException, SAXException, ParserConfigurationException { - projects = new Hashtable<>(); + projects.clear(); File home = getWorkFolder(); File list = new File(home, "projects.json"); if (!list.exists()) { @@ -519,8 +508,8 @@ private JSONObject getSegmentSource(String request) { JSONObject json = new JSONObject(request); String project = json.getString("project"); if (project == null) { - logger.log(Level.ERROR, "Null project requested"); - result.put(Constants.REASON, "Null project requested"); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.7")); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.7")); return result; } if (!projectStores.containsKey(project)) { @@ -530,7 +519,7 @@ private JSONObject getSegmentSource(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } @@ -549,8 +538,8 @@ private JSONObject getSegments(String request) { JSONObject json = new JSONObject(request); String project = json.getString("project"); if (project == null) { - logger.log(Level.ERROR, "Null project requested"); - result.put(Constants.REASON, "Null project requested"); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.7")); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.7")); return result; } @@ -561,15 +550,15 @@ private JSONObject getSegments(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } } XliffStore store = projectStores.get(project); if (store == null) { - logger.log(Level.ERROR, "Store is null"); - result.put(Constants.REASON, "Store is null"); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.8")); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.8")); return result; } String filterText = json.getString("filterText"); @@ -592,7 +581,7 @@ private JSONObject getSegments(String request) { } result.put("segments", array); } catch (IOException | SAXException | ParserConfigurationException | DataFormatException | SQLException e) { - logger.log(Level.ERROR, "Error loading segments", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.9"), e); result.put(Constants.REASON, e.getMessage()); } return result; @@ -609,7 +598,7 @@ private JSONObject getSegmentsCount(String request) { prj.getTargetLang().getCode()); projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } @@ -619,7 +608,7 @@ private JSONObject getSegmentsCount(String request) { result.put("count", store.size()); result.put("statistics", store.getTranslationStatus()); } catch (SQLException sql) { - logger.log(Level.ERROR, "Error retrieving count", sql); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.10"), sql); result.put(Constants.REASON, sql.getMessage()); } return result; @@ -638,9 +627,6 @@ private JSONObject createProject(String request) { String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); @@ -664,7 +650,7 @@ private JSONObject createProject(String request) { File projectFolder = new File(getWorkFolder(), id); Files.createDirectories(projectFolder.toPath()); List sourceFiles = new ArrayList<>(); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { List xliffs = new ArrayList<>(); for (int i = 0; i < files.length(); i++) { @@ -716,7 +702,8 @@ private JSONObject createProject(String request) { } if (!"0".equals(res.get(0))) { if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Conversion failed for: " + file.toString(2)); + MessageFormat mf = new MessageFormat(Messages.getString("ProjectsHandler.11")); + logger.log(Level.INFO, mf.format(new String[] { file.toString(2) })); } try { TmsServer.deleteFolder(projectFolder); @@ -766,7 +753,7 @@ private JSONObject createProject(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); result.put(Constants.STATUS, Constants.SUCCESS); } catch (IOException | JSONException | SAXException | ParserConfigurationException e) { logger.log(Level.ERROR, e); @@ -794,11 +781,11 @@ public static File getWorkFolder() throws IOException { private JSONObject closeProject(String request) { JSONObject result = new JSONObject(); if (projects == null) { - result.put(Constants.REASON, "Project list not loaded"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.12")); return result; } if (projectStores == null) { - result.put(Constants.REASON, "Projects map is null"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.13")); return result; } JSONObject json = new JSONObject(request); @@ -813,7 +800,7 @@ private JSONObject closeProject(String request) { result.put(Constants.REASON, e.getMessage()); } } else { - result.put(Constants.REASON, "Project is not open"); + result.put(Constants.REASON, Messages.getString("ProjectsHandler.14")); } return result; } @@ -821,7 +808,7 @@ private JSONObject closeProject(String request) { public static synchronized void closeAll() throws SQLException { if (projectStores == null) { if (TmsServer.isDebug()) { - logger.log(Level.INFO, "no open projects"); + logger.log(Level.INFO, Messages.getString("ProjectsHandler.15")); } return; } @@ -832,7 +819,7 @@ public static synchronized void closeAll() throws SQLException { } projectStores.clear(); if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Projects closed"); + logger.log(Level.INFO, Messages.getString("ProjectsHandler.16")); } } @@ -944,13 +931,10 @@ private JSONObject assembleMatchesAll(String request) { } String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { if (projectStores.containsKey(project)) { projectStores.get(project).assembleMatchesAll(json); @@ -964,7 +948,7 @@ private JSONObject assembleMatchesAll(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (IOException | SAXException | ParserConfigurationException | URISyntaxException | SQLException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); @@ -987,14 +971,11 @@ private JSONObject tmTranslateAll(String request) { } String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put("percentage", 0); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { obj.put("translated", projectStores.get(project).tmTranslateAll(memory, penalization, processes, id)); @@ -1007,7 +988,7 @@ private JSONObject tmTranslateAll(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (Exception e) { logger.log(Level.ERROR, e.getMessage(), e); result.put(Constants.REASON, e.getMessage()); @@ -1020,7 +1001,8 @@ private JSONObject getProjectMemories(String request) { JSONObject json = new JSONObject(request); try { result.put("memories", MemoriesHandler.getMemories()); - result.put("default", projects.get(json.getString("project")).getMemory()); + String memory = projects.get(json.getString("project")).getMemory(); + result.put("default", memory != null ? memory : ""); } catch (IOException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); @@ -1033,7 +1015,8 @@ private JSONObject getProjectGlossaries(String request) { JSONObject json = new JSONObject(request); try { result.put("glossaries", GlossariesHandler.getGlossaries()); - result.put("default", projects.get(json.getString("project")).getGlossary()); + String glossary = projects.get(json.getString("project")).getGlossary(); + result.put("default", glossary != null ? glossary : ""); } catch (IOException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); @@ -1092,9 +1075,6 @@ private JSONObject importXliff(String request) { JSONObject json = new JSONObject(request); String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); @@ -1102,9 +1082,9 @@ private JSONObject importXliff(String request) { String description = json.getString("project"); File xliffFile = new File(json.getString("xliff")); if (!xliffFile.exists()) { - throw new IOException("XLIFF file does not exist"); + throw new IOException(Messages.getString("ProjectsHandler.17")); } - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { JSONObject details = XliffUtils.getProjectDetails(xliffFile); Project p = new Project(id, description, Project.NEW, @@ -1138,7 +1118,7 @@ private JSONObject importXliff(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (IOException e) { logger.log(Level.ERROR, e); result.put(Constants.STATUS, Constants.ERROR); @@ -1269,13 +1249,10 @@ private JSONObject confirmAllTranslations(String request) { try { String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { JSONObject json = new JSONObject(request); String project = json.getString("project"); @@ -1295,7 +1272,7 @@ private JSONObject confirmAllTranslations(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); } catch (JSONException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); @@ -1334,7 +1311,7 @@ private JSONObject generateStatistics(String request) { projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } @@ -1362,7 +1339,7 @@ private JSONObject exportHTML(String request) { projectStores.put(project, store); } catch (SAXException | IOException | ParserConfigurationException | URISyntaxException | SQLException e) { - logger.log(Level.ERROR, "Error creating project store", e); + logger.log(Level.ERROR, Messages.getString("ProjectsHandler.3"), e); result.put(Constants.REASON, e.getMessage()); return result; } @@ -1398,13 +1375,10 @@ private JSONObject applyMtAll(String request) { JSONObject result = new JSONObject(); String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { JSONObject json = new JSONObject(request); String project = json.getString("project"); @@ -1419,7 +1393,7 @@ private JSONObject applyMtAll(String request) { obj.put(Constants.REASON, e.getMessage()); processes.put(id, obj); } - }).start(); + }); return result; } @@ -1449,7 +1423,7 @@ private JSONObject getTerms(String request) { if (projectStores.containsKey(project)) { result.put("terms", projectStores.get(project).getTerms(json)); } - } catch (SQLException | IOException e) { + } catch (SQLException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); } @@ -1477,14 +1451,11 @@ private JSONObject getProjectTerms(String request) { final JSONObject json = new JSONObject(request); String id = "" + System.currentTimeMillis(); result.put("process", id); - if (processes == null) { - processes = new Hashtable<>(); - } JSONObject obj = new JSONObject(); obj.put(Constants.PROGRESS, Constants.PROCESSING); processes.put(id, obj); try { - new Thread(() -> { + Thread.ofVirtual().start(() -> { try { String project = json.getString("project"); if (!projectStores.containsKey(project)) { @@ -1501,7 +1472,7 @@ private JSONObject getProjectTerms(String request) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); } - }).start(); + }); } catch (JSONException e) { result.put(Constants.REASON, e.getMessage()); } @@ -1622,7 +1593,7 @@ private JSONObject getNotes(String request) { result.put("notes", projectStores.get(project).getNotes(json.getString("file"), json.getString("unit"), json.getString("segment"))); } - } catch (SQLException | IOException e) { + } catch (SQLException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); } @@ -1638,7 +1609,7 @@ private JSONObject addNote(String request) { result.put("notes", projectStores.get(project).addNote(json.getString("file"), json.getString("unit"), json.getString("segment"), json.getString("noteText"))); } - } catch (SQLException | IOException e) { + } catch (SQLException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); } @@ -1654,7 +1625,7 @@ private JSONObject removeNote(String request) { result.put("notes", projectStores.get(project).removeNote(json.getString("file"), json.getString("unit"), json.getString("segment"), json.getString("noteId"))); } - } catch (SQLException | IOException e) { + } catch (SQLException e) { logger.log(Level.ERROR, e); result.put(Constants.REASON, e.getMessage()); } diff --git a/src/com/maxprograms/swordfish/ServicesHandler.java b/src/com/maxprograms/swordfish/ServicesHandler.java index d103d4e..c2c2102 100644 --- a/src/com/maxprograms/swordfish/ServicesHandler.java +++ b/src/com/maxprograms/swordfish/ServicesHandler.java @@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; +import java.text.MessageFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -60,592 +61,598 @@ public class ServicesHandler implements HttpHandler { - private static Logger logger = System.getLogger(ServicesHandler.class.getName()); - - private static JSONObject clients; - private static JSONObject subjects; - private static JSONObject projects; - - @Override - public void handle(HttpExchange exchange) throws IOException { - try { - String request; - URI uri = exchange.getRequestURI(); - try (InputStream is = exchange.getRequestBody()) { - request = TmsServer.readRequestBody(is); - } - JSONObject response = processRequest(uri.toString(), request); - byte[] bytes = response.toString().getBytes(StandardCharsets.UTF_8); - exchange.sendResponseHeaders(200, bytes.length); - exchange.getResponseHeaders().add("content-type", "application/json; charset=utf-8"); - try (ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) { - try (OutputStream os = exchange.getResponseBody()) { - byte[] array = new byte[2048]; - int read; - while ((read = stream.read(array)) != -1) { - os.write(array, 0, read); - } - } - } - } catch (IOException e) { - logger.log(Level.ERROR, "Error processing service request" + exchange.getRequestURI().toString(), e); - } - } - - private JSONObject processRequest(String url, String request) { - JSONObject result = null; - try { - if ("/services/getLanguages".equals(url)) { - result = getLanguages(); - } else if ("/services/getFileTypes".equals(url)) { - result = getFileFormats(); - } else if ("/services/getCharsets".equals(url)) { - result = getCharsets(); - } else if ("/services/getFileType".equals(url)) { - result = getFileType(request); - } else if ("/services/getClients".equals(url)) { - result = getClients(); - } else if ("/services/getSubjects".equals(url)) { - result = getSubjects(); - } else if ("/services/getProjects".equals(url)) { - result = getProjects(); - } else if ("/services/getSpellingLanguages".equals(url)) { - result = getSpellingLanguages(request); - } else if ("/services/remoteDatabases".equals(url)) { - result = RemoteUtils.remoteDatabases(request); - } else if ("/services/addDatabases".equals(url)) { - result = addDatabases(request); - } else if ("/services/xmlFilters".equals(url)) { - result = getXmlFilters(request); - } else if ("/services/importFilter".equals(url)) { - result = importXmlFilter(request); - } else if ("/services/removeFilters".equals(url)) { - result = removeFilters(request); - } else if ("/services/exportFilters".equals(url)) { - result = exportFilters(request); - } else if ("/services/addFilter".equals(url)) { - result = addFilter(request); - } else if ("/services/filterData".equals(url)) { - result = getFilterData(request); - } else if ("/services/saveElement".equals(url)) { - result = saveElement(request); - } else if ("/services/removeElements".equals(url)) { - result = removeElements(request); - } else if ("/services/systemInfo".equals(url)) { - result = getSystemInformation(); - } else { - result = new JSONObject(); - result.put("url", url); - result.put("request", request); - result.put(Constants.REASON, "Unknown request"); - } - if (!result.has(Constants.REASON)) { - result.put(Constants.STATUS, Constants.SUCCESS); - } else { - result.put(Constants.STATUS, Constants.ERROR); - } - } catch (Exception j) { - logger.log(Level.ERROR, j.getMessage(), j); - result = new JSONObject(); - result.put(Constants.STATUS, Constants.ERROR); - result.put(Constants.REASON, j.getMessage()); - } - return result; - } - - private JSONObject getFileFormats() { - JSONObject result = new JSONObject(); - JSONArray array = new JSONArray(); - String[] formats = FileFormats.getFormats(); - for (int i = 0; i < formats.length; i++) { - String format = formats[i]; - JSONObject json = new JSONObject(); - json.put("code", FileFormats.getShortName(format)); - json.put("description", format); - array.put(json); - } - result.put("formats", array); - return result; - } - - private JSONObject getCharsets() { - JSONObject result = new JSONObject(); - JSONArray array = new JSONArray(); - TreeMap charsets = new TreeMap<>(Charset.availableCharsets()); - Set keys = charsets.keySet(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - String charset = it.next(); - JSONObject json = new JSONObject(); - json.put("code", charset); - json.put("description", charsets.get(charset).displayName()); - array.put(json); - } - result.put("charsets", array); - return result; - } - - private JSONObject addDatabases(String request) throws ParseException, IOException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - String server = json.getString("server"); - String user = json.getString("user"); - String password = json.getString("password"); - String type = json.getString("type"); - JSONArray array = json.getJSONArray("databases"); - SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm"); - for (int i = 0; i < array.length(); i++) { - JSONObject obj = array.getJSONObject(i); - Date creationDate = df.parse(obj.getString("creationDate")); - Memory mem = new Memory(obj.getString("id"), obj.getString("name"), obj.getString("project"), - obj.getString("subject"), obj.getString("client"), creationDate, Memory.REMOTE, server, user, - password); - if ("memory".equals(type)) { - MemoriesHandler.addMemory(mem); - } else { - GlossariesHandler.addGlossary(mem); - } - } - return result; - } - - private JSONObject getXmlFilters(String request) { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - JSONArray array = new JSONArray(); - List list = new ArrayList<>(); - String[] files = xmlFiltersFolder.list(); - for (int i = 0; i < files.length; i++) { - if (files[i].endsWith(".xml")) { - list.add(files[i]); - } - } - Collections.sort(list, (o1, o2) -> o1.compareToIgnoreCase(o2)); - for (int i = 0; i < list.size(); i++) { - array.put(list.get(i)); - } - result.put("files", array); - return result; - } - - private JSONObject importXmlFilter(String request) { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File file = new File(json.getString("file")); - File targetFile = new File(xmlFiltersFolder, file.getName()); - try { - SAXBuilder builder = new SAXBuilder(); - builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); - Document doc = builder.build(file); - if (!"ini-file".equals(doc.getRootElement().getName())) { - throw new IOException("Incorrect file type"); - } - Files.copy(file.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (IOException | SAXException | ParserConfigurationException | URISyntaxException e) { - logger.log(Level.ERROR, "Error importing xml configuration", e); - result.put(Constants.REASON, e.getMessage()); - } - return result; - } - - private JSONObject removeFilters(String request) throws IOException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - JSONArray files = json.getJSONArray("files"); - for (int i = 0; i < files.length(); i++) { - File filter = new File(xmlFiltersFolder, files.getString(i)); - Files.delete(filter.toPath()); - } - return result; - } - - private JSONObject exportFilters(String request) throws IOException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File targetFolder = new File(json.getString("folder")); - if (targetFolder.isFile()) { - targetFolder = targetFolder.getParentFile(); - } - JSONArray files = json.getJSONArray("files"); - for (int i = 0; i < files.length(); i++) { - File source = new File(xmlFiltersFolder, files.getString(i)); - File target = new File(targetFolder, files.getString(i)); - Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - return result; - } - - private JSONObject addFilter(String request) throws IOException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File configFile = new File(xmlFiltersFolder, "config_" + json.getString("root") + ".xml"); - if (configFile.exists()) { - result.put(Constants.REASON, "Configuration exists"); - } else { - Document doc = new Document(null, "ini-file", "-//MAXPROGRAMS//Converters 2.0.0//EN", "configuration.dtd"); - XMLOutputter outputter = new XMLOutputter(); - try (FileOutputStream out = new FileOutputStream(configFile)) { - outputter.output(doc, out); - } - } - return result; - } - - private JSONObject getFilterData(String request) - throws SAXException, IOException, ParserConfigurationException, URISyntaxException { - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File configFile = new File(xmlFiltersFolder, json.getString("file")); - SAXBuilder builder = new SAXBuilder(); - builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); - Document doc = builder.build(configFile); - return toJSON(doc.getRootElement()); - } - - private JSONObject saveElement(String request) - throws SAXException, IOException, ParserConfigurationException, URISyntaxException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File configFile = new File(xmlFiltersFolder, json.getString("filter")); - SAXBuilder builder = new SAXBuilder(); - builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); - Document doc = builder.build(configFile); - Element root = doc.getRootElement(); - List tags = root.getChildren("tag"); - Iterator it = tags.iterator(); - boolean found = false; - while (it.hasNext()) { - Element tag = it.next(); - if (tag.getText().equals(json.getString("name"))) { - found = true; - tag.setAttribute("hard-break", json.getString("type")); - String attributes = json.getString("attributes"); - if (!attributes.isBlank()) { - tag.setAttribute("attributes", attributes); - } else { - tag.removeAttribute("attributes"); - } - String keepSpace = json.getString("keepSpace"); - if ("yes".equals(keepSpace)) { - tag.setAttribute("keep-format", keepSpace); - } else { - tag.removeAttribute("keep-format"); - } - if ("inline".equals(json.getString("type"))) { - tag.setAttribute("ctype", json.getString("inline")); - } else { - tag.removeAttribute("ctype"); - } - } - } - if (!found) { - Element tag = new Element("tag"); - tag.setText(json.getString("name")); - tag.setAttribute("hard-break", json.getString("type")); - String attributes = json.getString("attributes"); - if (!attributes.isBlank()) { - tag.setAttribute("attributes", attributes); - } - String keepSpace = json.getString("keepSpace"); - if ("yes".equals(keepSpace)) { - tag.setAttribute("keep-format", keepSpace); - } - if ("inline".equals(json.getString("type"))) { - tag.setAttribute("ctype", json.getString("inline")); - } - tags.add(tag); - } - Collections.sort(tags, (o1, o2) -> o1.getText().compareTo(o2.getText())); - root.setChildren(tags); - Indenter.indent(root, 0); - XMLOutputter outputter = new XMLOutputter(); - outputter.preserveSpace(true); - try (FileOutputStream out = new FileOutputStream(configFile)) { - outputter.output(doc, out); - } - return result; - } - - private JSONObject removeElements(String request) - throws SAXException, IOException, ParserConfigurationException, URISyntaxException { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - Set names = new TreeSet<>(); - JSONArray array = json.getJSONArray("elements"); - for (int i = 0; i < array.length(); i++) { - names.add(array.getString(i)); - } - File appFolder = new File(json.getString("path")); - File xmlFiltersFolder = new File(appFolder, "xmlfilter"); - File configFile = new File(xmlFiltersFolder, json.getString("filter")); - SAXBuilder builder = new SAXBuilder(); - builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); - Document doc = builder.build(configFile); - Element root = doc.getRootElement(); - List tags = root.getChildren("tag"); - List newList = new ArrayList<>(); - Iterator it = tags.iterator(); - while (it.hasNext()) { - Element tag = it.next(); - if (!names.contains(tag.getText())) { - newList.add(tag); - } - } - Collections.sort(newList, (o1, o2) -> o1.getText().compareTo(o2.getText())); - root.setChildren(newList); - Indenter.indent(root, 0); - XMLOutputter outputter = new XMLOutputter(); - outputter.preserveSpace(true); - try (FileOutputStream out = new FileOutputStream(configFile)) { - outputter.output(doc, out); - } - return result; - } - - private JSONObject getFileType(String request) { - JSONObject result = new JSONObject(); - JSONObject json = new JSONObject(request); - JSONArray files = json.getJSONArray("files"); - JSONArray detailsArray = new JSONArray(); - for (int i = 0; i < files.length(); i++) { - String file = files.getString(i); - String type = "Unknown"; - String encoding = "Unknown"; - String detected = FileFormats.detectFormat(file); - if (detected != null) { - type = FileFormats.getShortName(detected); - if (type != null) { - Charset charset = EncodingResolver.getEncoding(file, detected); - if (charset != null) { - encoding = charset.name(); - } - } - } - if (encoding.equals("Unknown")) { - try { - Charset bom = EncodingResolver.getBOM(file); - if (bom != null) { - encoding = bom.name(); - } - } catch (IOException e) { - // ignore - } - } - JSONObject details = new JSONObject(); - details.put("file", file); - details.put("type", type); - details.put("encoding", encoding); - detailsArray.put(details); - } - result.put("files", detailsArray); - return result; - } - - private JSONObject getLanguages() { - JSONObject result = new JSONObject(); - try { - List languages = LanguageUtils.getCommonLanguages(); - JSONArray array = new JSONArray(); - for (int i = 0; i < languages.size(); i++) { - Language lang = languages.get(i); - JSONObject json = new JSONObject(); - json.put("code", lang.getCode()); - json.put("description", lang.getDescription()); - array.put(json); - } - result.put("languages", array); - } catch (SAXException | IOException | ParserConfigurationException e) { - logger.log(Level.ERROR, "Error getting languages", e); - result.put(Constants.REASON, e.getMessage()); - } - return result; - } - - private static JSONObject getClients() throws IOException { - if (clients != null) { - return clients; - } - File clientsFile = new File(TmsServer.getWorkFolder(), "clients.json"); - if (!clientsFile.exists()) { - clients = new JSONObject(); - clients.put("clients", new JSONArray()); - return clients; - } - clients = TmsServer.readJSON(clientsFile); - return clients; - } - - private static JSONObject getSubjects() throws IOException { - if (subjects != null) { - return subjects; - } - File subjectsFile = new File(TmsServer.getWorkFolder(), "subjects.json"); - if (!subjectsFile.exists()) { - subjects = new JSONObject(); - subjects.put("subjects", new JSONArray()); - return subjects; - } - subjects = TmsServer.readJSON(subjectsFile); - return subjects; - } - - private static JSONObject getSystemInformation() { - JSONObject result = new JSONObject(); - result.put("swordfish", Constants.VERSION + " Build: " + Constants.BUILD); - result.put("openxliff", - com.maxprograms.converters.Constants.VERSION + " Build: " + com.maxprograms.converters.Constants.BUILD); - result.put("xmljava", com.maxprograms.xml.Constants.VERSION + " Build: " + com.maxprograms.xml.Constants.BUILD); - result.put("java", System.getProperty("java.version") + " Vendor: " + System.getProperty("java.vendor")); - return result; - } - - private static JSONObject getProjects() throws IOException { - if (projects != null) { - return projects; - } - File projectsFile = new File(TmsServer.getWorkFolder(), "projects.json"); - if (!projectsFile.exists()) { - projects = new JSONObject(); - projects.put("projects", new JSONArray()); - try (FileOutputStream out = new FileOutputStream(projectsFile)) { - out.write(projects.toString().getBytes(StandardCharsets.UTF_8)); - } - return projects; - } - projects = TmsServer.readJSON(projectsFile); - return projects; - } - - public static void addClient(String client) throws IOException { - if (client == null || client.isEmpty()) { - return; - } - getClients(); - JSONArray array = clients.getJSONArray("clients"); - for (int i = 0; i < array.length(); i++) { - if (client.equals(array.getString(i))) { - return; - } - } - clients.put("clients", insertString(client, array)); - File clientsFile = new File(TmsServer.getWorkFolder(), "clients.json"); - try (FileOutputStream out = new FileOutputStream(clientsFile)) { - out.write(clients.toString().getBytes(StandardCharsets.UTF_8)); - } - } - - public static void addSubject(String subject) throws IOException { - if (subject == null || subject.isEmpty()) { - return; - } - getSubjects(); - JSONArray array = subjects.getJSONArray("subjects"); - for (int i = 0; i < array.length(); i++) { - if (subject.equals(array.getString(i))) { - return; - } - } - subjects.put("subjects", insertString(subject, array)); - File subjectsFile = new File(TmsServer.getWorkFolder(), "subjects.json"); - try (FileOutputStream out = new FileOutputStream(subjectsFile)) { - out.write(subjects.toString().getBytes(StandardCharsets.UTF_8)); - } - } - - public static void addProject(String project) throws IOException { - if (project == null || project.isEmpty()) { - return; - } - getProjects(); - JSONArray array = projects.getJSONArray("projects"); - for (int i = 0; i < array.length(); i++) { - if (project.equals(array.getString(i))) { - return; - } - } - projects.put("projects", insertString(project, array)); - File projectsFile = new File(TmsServer.getProjectsFolder(), "projects.json"); - try (FileOutputStream out = new FileOutputStream(projectsFile)) { - out.write(projects.toString().getBytes(StandardCharsets.UTF_8)); - } - } - - private static JSONArray insertString(String string, JSONArray array) { - JSONArray result = new JSONArray(); - List list = new ArrayList<>(); - list.add(string); - for (int i = 0; i < array.length(); i++) { - list.add(array.getString(i)); - } - Collections.sort(list); - Iterator it = list.iterator(); - while (it.hasNext()) { - result.put(it.next()); - } - return result; - } - - private JSONObject getSpellingLanguages(String request) { - JSONObject result = new JSONObject(); - try { - JSONArray array = new JSONArray(); - JSONObject json = new JSONObject(request); - JSONArray languages = json.getJSONArray("languages"); - for (int i = 0; i < languages.length(); i++) { - String code = languages.getString(i); - JSONArray a = new JSONArray(); - a.put(code); - a.put(LanguageUtils.getLanguage(code)); - array.put(a); - } - result.put("languages", array); - } catch (IOException | SAXException | ParserConfigurationException e) { - logger.log(Level.ERROR, e); - result.put(Constants.REASON, e.getMessage()); - } - return result; - } - - private JSONObject toJSON(Element e) { - JSONObject result = new JSONObject(); - result.put("name", e.getName()); - JSONArray attributes = new JSONArray(); - List atts = e.getAttributes(); - for (int i = 0; i < atts.size(); i++) { - Attribute a = atts.get(i); - JSONArray o = new JSONArray(); - o.put(a.getName()); - o.put(a.getValue()); - attributes.put(o); - } - if (attributes.length() > 0) { - result.put("attributes", attributes); - } - JSONArray array = new JSONArray(); - List children = e.getChildren(); - for (int i = 0; i < children.size(); i++) { - array.put(toJSON(children.get(i))); - } - if (array.length() > 0) { - result.put("children", array); - } - String text = e.getText().trim(); - if (!text.isBlank()) { - result.put("content", text); - } - return result; - } + private static Logger logger = System.getLogger(ServicesHandler.class.getName()); + + private static JSONObject clients; + private static JSONObject subjects; + private static JSONObject projects; + + @Override + public void handle(HttpExchange exchange) throws IOException { + try { + String request; + URI uri = exchange.getRequestURI(); + try (InputStream is = exchange.getRequestBody()) { + request = TmsServer.readRequestBody(is); + } + JSONObject response = processRequest(uri.toString(), request); + byte[] bytes = response.toString().getBytes(StandardCharsets.UTF_8); + exchange.sendResponseHeaders(200, bytes.length); + exchange.getResponseHeaders().add("content-type", "application/json; charset=utf-8"); + try (ByteArrayInputStream stream = new ByteArrayInputStream(bytes)) { + try (OutputStream os = exchange.getResponseBody()) { + byte[] array = new byte[2048]; + int read; + while ((read = stream.read(array)) != -1) { + os.write(array, 0, read); + } + } + } + } catch (IOException e) { + MessageFormat mf = new MessageFormat(Messages.getString("ServicesHandler.0")); + logger.log(Level.ERROR, mf.format(new String[] { exchange.getRequestURI().toString() }), e); + } + } + + private JSONObject processRequest(String url, String request) { + JSONObject result = null; + try { + if ("/services/getLanguages".equals(url)) { + result = getLanguages(); + } else if ("/services/getFileTypes".equals(url)) { + result = getFileFormats(); + } else if ("/services/getCharsets".equals(url)) { + result = getCharsets(); + } else if ("/services/getFileType".equals(url)) { + result = getFileType(request); + } else if ("/services/getClients".equals(url)) { + result = getClients(); + } else if ("/services/getSubjects".equals(url)) { + result = getSubjects(); + } else if ("/services/getProjects".equals(url)) { + result = getProjects(); + } else if ("/services/getSpellingLanguages".equals(url)) { + result = getSpellingLanguages(request); + } else if ("/services/remoteDatabases".equals(url)) { + result = RemoteUtils.remoteDatabases(request); + } else if ("/services/addDatabases".equals(url)) { + result = addDatabases(request); + } else if ("/services/xmlFilters".equals(url)) { + result = getXmlFilters(request); + } else if ("/services/importFilter".equals(url)) { + result = importXmlFilter(request); + } else if ("/services/removeFilters".equals(url)) { + result = removeFilters(request); + } else if ("/services/exportFilters".equals(url)) { + result = exportFilters(request); + } else if ("/services/addFilter".equals(url)) { + result = addFilter(request); + } else if ("/services/filterData".equals(url)) { + result = getFilterData(request); + } else if ("/services/saveElement".equals(url)) { + result = saveElement(request); + } else if ("/services/removeElements".equals(url)) { + result = removeElements(request); + } else if ("/services/systemInfo".equals(url)) { + result = getSystemInformation(); + } else { + result = new JSONObject(); + result.put("url", url); + result.put("request", request); + MessageFormat mf = new MessageFormat(Messages.getString("ServicesHandler.1")); + result.put(Constants.REASON, mf.format(new String[] { url })); + } + if (!result.has(Constants.REASON)) { + result.put(Constants.STATUS, Constants.SUCCESS); + } else { + result.put(Constants.STATUS, Constants.ERROR); + } + } catch (Exception j) { + logger.log(Level.ERROR, j.getMessage(), j); + result = new JSONObject(); + result.put(Constants.STATUS, Constants.ERROR); + result.put(Constants.REASON, j.getMessage()); + } + return result; + } + + private JSONObject getFileFormats() { + JSONObject result = new JSONObject(); + JSONArray array = new JSONArray(); + String[] formats = FileFormats.getFormats(); + for (int i = 0; i < formats.length; i++) { + String format = formats[i]; + JSONObject json = new JSONObject(); + json.put("code", FileFormats.getShortName(format)); + json.put("description", format); + array.put(json); + } + result.put("formats", array); + return result; + } + + private JSONObject getCharsets() { + JSONObject result = new JSONObject(); + JSONArray array = new JSONArray(); + TreeMap charsets = new TreeMap<>(Charset.availableCharsets()); + Set keys = charsets.keySet(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + String charset = it.next(); + JSONObject json = new JSONObject(); + json.put("code", charset); + json.put("description", charsets.get(charset).displayName()); + array.put(json); + } + result.put("charsets", array); + return result; + } + + private JSONObject addDatabases(String request) throws ParseException, IOException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + String server = json.getString("server"); + String user = json.getString("user"); + String password = json.getString("password"); + String type = json.getString("type"); + JSONArray array = json.getJSONArray("databases"); + SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm"); + for (int i = 0; i < array.length(); i++) { + JSONObject obj = array.getJSONObject(i); + Date creationDate = df.parse(obj.getString("creationDate")); + Memory mem = new Memory(obj.getString("id"), obj.getString("name"), obj.getString("project"), + obj.getString("subject"), obj.getString("client"), creationDate, Memory.REMOTE, server, user, + password); + if ("memory".equals(type)) { + MemoriesHandler.addMemory(mem); + } else { + GlossariesHandler.addGlossary(mem); + } + } + return result; + } + + private JSONObject getXmlFilters(String request) { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + JSONArray array = new JSONArray(); + List list = new ArrayList<>(); + String[] files = xmlFiltersFolder.list(); + for (int i = 0; i < files.length; i++) { + if (files[i].endsWith(".xml")) { + list.add(files[i]); + } + } + Collections.sort(list, (o1, o2) -> o1.compareToIgnoreCase(o2)); + for (int i = 0; i < list.size(); i++) { + array.put(list.get(i)); + } + result.put("files", array); + return result; + } + + private JSONObject importXmlFilter(String request) { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File file = new File(json.getString("file")); + File targetFile = new File(xmlFiltersFolder, file.getName()); + try { + SAXBuilder builder = new SAXBuilder(); + builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); + Document doc = builder.build(file); + if (!"ini-file".equals(doc.getRootElement().getName())) { + throw new IOException(Messages.getString("ServicesHandler.2")); + } + Files.copy(file.toPath(), targetFile.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException | SAXException | ParserConfigurationException | URISyntaxException e) { + logger.log(Level.ERROR, Messages.getString("ServicesHandler.3"), e); + result.put(Constants.REASON, e.getMessage()); + } + return result; + } + + private JSONObject removeFilters(String request) throws IOException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + JSONArray files = json.getJSONArray("files"); + for (int i = 0; i < files.length(); i++) { + File filter = new File(xmlFiltersFolder, files.getString(i)); + Files.delete(filter.toPath()); + } + return result; + } + + private JSONObject exportFilters(String request) throws IOException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File targetFolder = new File(json.getString("folder")); + if (targetFolder.isFile()) { + targetFolder = targetFolder.getParentFile(); + } + JSONArray files = json.getJSONArray("files"); + for (int i = 0; i < files.length(); i++) { + File source = new File(xmlFiltersFolder, files.getString(i)); + File target = new File(targetFolder, files.getString(i)); + Files.copy(source.toPath(), target.toPath(), StandardCopyOption.REPLACE_EXISTING); + } + return result; + } + + private JSONObject addFilter(String request) throws IOException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File configFile = new File(xmlFiltersFolder, "config_" + json.getString("root") + ".xml"); + if (configFile.exists()) { + result.put(Constants.REASON, Messages.getString("ServicesHandler.4")); + } else { + Document doc = new Document(null, "ini-file", "-//MAXPROGRAMS//Converters 2.0.0//EN", "configuration.dtd"); + XMLOutputter outputter = new XMLOutputter(); + try (FileOutputStream out = new FileOutputStream(configFile)) { + outputter.output(doc, out); + } + } + return result; + } + + private JSONObject getFilterData(String request) + throws SAXException, IOException, ParserConfigurationException, URISyntaxException { + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File configFile = new File(xmlFiltersFolder, json.getString("file")); + SAXBuilder builder = new SAXBuilder(); + builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); + Document doc = builder.build(configFile); + return toJSON(doc.getRootElement()); + } + + private JSONObject saveElement(String request) + throws SAXException, IOException, ParserConfigurationException, URISyntaxException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File configFile = new File(xmlFiltersFolder, json.getString("filter")); + SAXBuilder builder = new SAXBuilder(); + builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); + Document doc = builder.build(configFile); + Element root = doc.getRootElement(); + List tags = root.getChildren("tag"); + Iterator it = tags.iterator(); + boolean found = false; + while (it.hasNext()) { + Element tag = it.next(); + if (tag.getText().equals(json.getString("name"))) { + found = true; + tag.setAttribute("hard-break", json.getString("type")); + String attributes = json.getString("attributes"); + if (!attributes.isBlank()) { + tag.setAttribute("attributes", attributes); + } else { + tag.removeAttribute("attributes"); + } + String keepSpace = json.getString("keepSpace"); + if ("yes".equals(keepSpace)) { + tag.setAttribute("keep-format", keepSpace); + } else { + tag.removeAttribute("keep-format"); + } + if ("inline".equals(json.getString("type"))) { + tag.setAttribute("ctype", json.getString("inline")); + } else { + tag.removeAttribute("ctype"); + } + } + } + if (!found) { + Element tag = new Element("tag"); + tag.setText(json.getString("name")); + tag.setAttribute("hard-break", json.getString("type")); + String attributes = json.getString("attributes"); + if (!attributes.isBlank()) { + tag.setAttribute("attributes", attributes); + } + String keepSpace = json.getString("keepSpace"); + if ("yes".equals(keepSpace)) { + tag.setAttribute("keep-format", keepSpace); + } + if ("inline".equals(json.getString("type"))) { + tag.setAttribute("ctype", json.getString("inline")); + } + tags.add(tag); + } + Collections.sort(tags, (o1, o2) -> o1.getText().compareTo(o2.getText())); + root.setChildren(tags); + Indenter.indent(root, 0); + XMLOutputter outputter = new XMLOutputter(); + outputter.preserveSpace(true); + try (FileOutputStream out = new FileOutputStream(configFile)) { + outputter.output(doc, out); + } + return result; + } + + private JSONObject removeElements(String request) + throws SAXException, IOException, ParserConfigurationException, URISyntaxException { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + Set names = new TreeSet<>(); + JSONArray array = json.getJSONArray("elements"); + for (int i = 0; i < array.length(); i++) { + names.add(array.getString(i)); + } + File appFolder = new File(json.getString("path")); + File xmlFiltersFolder = new File(appFolder, "xmlfilter"); + File configFile = new File(xmlFiltersFolder, json.getString("filter")); + SAXBuilder builder = new SAXBuilder(); + builder.setEntityResolver(new Catalog(TmsServer.getCatalogFile())); + Document doc = builder.build(configFile); + Element root = doc.getRootElement(); + List tags = root.getChildren("tag"); + List newList = new ArrayList<>(); + Iterator it = tags.iterator(); + while (it.hasNext()) { + Element tag = it.next(); + if (!names.contains(tag.getText())) { + newList.add(tag); + } + } + Collections.sort(newList, (o1, o2) -> o1.getText().compareTo(o2.getText())); + root.setChildren(newList); + Indenter.indent(root, 0); + XMLOutputter outputter = new XMLOutputter(); + outputter.preserveSpace(true); + try (FileOutputStream out = new FileOutputStream(configFile)) { + outputter.output(doc, out); + } + return result; + } + + private JSONObject getFileType(String request) { + JSONObject result = new JSONObject(); + JSONObject json = new JSONObject(request); + JSONArray files = json.getJSONArray("files"); + JSONArray detailsArray = new JSONArray(); + for (int i = 0; i < files.length(); i++) { + String file = files.getString(i); + String type = "Unknown"; + String encoding = "Unknown"; + String detected = FileFormats.detectFormat(file); + if (detected != null) { + type = FileFormats.getShortName(detected); + if (type != null) { + Charset charset = EncodingResolver.getEncoding(file, detected); + if (charset != null) { + encoding = charset.name(); + } + } + } + if (encoding.equals("Unknown")) { + try { + Charset bom = EncodingResolver.getBOM(file); + if (bom != null) { + encoding = bom.name(); + } + } catch (IOException e) { + // ignore + } + } + JSONObject details = new JSONObject(); + details.put("file", file); + details.put("type", type); + details.put("encoding", encoding); + detailsArray.put(details); + } + result.put("files", detailsArray); + return result; + } + + private JSONObject getLanguages() { + JSONObject result = new JSONObject(); + try { + List languages = LanguageUtils.getCommonLanguages(); + JSONArray array = new JSONArray(); + for (int i = 0; i < languages.size(); i++) { + Language lang = languages.get(i); + JSONObject json = new JSONObject(); + json.put("code", lang.getCode()); + json.put("description", lang.getDescription()); + array.put(json); + } + result.put("languages", array); + } catch (SAXException | IOException | ParserConfigurationException e) { + logger.log(Level.ERROR, Messages.getString("ServicesHandler.5"), e); + result.put(Constants.REASON, e.getMessage()); + } + return result; + } + + private static JSONObject getClients() throws IOException { + if (clients != null) { + return clients; + } + File clientsFile = new File(TmsServer.getWorkFolder(), "clients.json"); + if (!clientsFile.exists()) { + clients = new JSONObject(); + clients.put("clients", new JSONArray()); + return clients; + } + clients = TmsServer.readJSON(clientsFile); + return clients; + } + + private static JSONObject getSubjects() throws IOException { + if (subjects != null) { + return subjects; + } + File subjectsFile = new File(TmsServer.getWorkFolder(), "subjects.json"); + if (!subjectsFile.exists()) { + subjects = new JSONObject(); + subjects.put("subjects", new JSONArray()); + return subjects; + } + subjects = TmsServer.readJSON(subjectsFile); + return subjects; + } + + private static JSONObject getSystemInformation() { + JSONObject result = new JSONObject(); + MessageFormat mf = new MessageFormat(Messages.getString("ServicesHandler.6")); + result.put("swordfish", mf.format(new String[] { Constants.VERSION, Constants.BUILD, })); + result.put("openxliff", mf.format(new String[] { com.maxprograms.converters.Constants.VERSION, + com.maxprograms.converters.Constants.BUILD })); + result.put("xmljava", + mf.format(new String[] { com.maxprograms.xml.Constants.VERSION, com.maxprograms.xml.Constants.BUILD })); + mf = new MessageFormat(Messages.getString("ServicesHandler.7")); + result.put("java", + mf.format(new String[] { System.getProperty("java.version"), System.getProperty("java.vendor") })); + return result; + } + + private static JSONObject getProjects() throws IOException { + if (projects != null) { + return projects; + } + File projectsFile = new File(TmsServer.getWorkFolder(), "projects.json"); + if (!projectsFile.exists()) { + projects = new JSONObject(); + projects.put("projects", new JSONArray()); + try (FileOutputStream out = new FileOutputStream(projectsFile)) { + out.write(projects.toString().getBytes(StandardCharsets.UTF_8)); + } + return projects; + } + projects = TmsServer.readJSON(projectsFile); + return projects; + } + + public static void addClient(String client) throws IOException { + if (client == null || client.isEmpty()) { + return; + } + getClients(); + JSONArray array = clients.getJSONArray("clients"); + for (int i = 0; i < array.length(); i++) { + if (client.equals(array.getString(i))) { + return; + } + } + clients.put("clients", insertString(client, array)); + File clientsFile = new File(TmsServer.getWorkFolder(), "clients.json"); + try (FileOutputStream out = new FileOutputStream(clientsFile)) { + out.write(clients.toString().getBytes(StandardCharsets.UTF_8)); + } + } + + public static void addSubject(String subject) throws IOException { + if (subject == null || subject.isEmpty()) { + return; + } + getSubjects(); + JSONArray array = subjects.getJSONArray("subjects"); + for (int i = 0; i < array.length(); i++) { + if (subject.equals(array.getString(i))) { + return; + } + } + subjects.put("subjects", insertString(subject, array)); + File subjectsFile = new File(TmsServer.getWorkFolder(), "subjects.json"); + try (FileOutputStream out = new FileOutputStream(subjectsFile)) { + out.write(subjects.toString().getBytes(StandardCharsets.UTF_8)); + } + } + + public static void addProject(String project) throws IOException { + if (project == null || project.isEmpty()) { + return; + } + getProjects(); + JSONArray array = projects.getJSONArray("projects"); + for (int i = 0; i < array.length(); i++) { + if (project.equals(array.getString(i))) { + return; + } + } + projects.put("projects", insertString(project, array)); + File projectsFile = new File(TmsServer.getProjectsFolder(), "projects.json"); + try (FileOutputStream out = new FileOutputStream(projectsFile)) { + out.write(projects.toString().getBytes(StandardCharsets.UTF_8)); + } + } + + private static JSONArray insertString(String string, JSONArray array) { + JSONArray result = new JSONArray(); + List list = new ArrayList<>(); + list.add(string); + for (int i = 0; i < array.length(); i++) { + list.add(array.getString(i)); + } + Collections.sort(list); + Iterator it = list.iterator(); + while (it.hasNext()) { + result.put(it.next()); + } + return result; + } + + private JSONObject getSpellingLanguages(String request) { + JSONObject result = new JSONObject(); + try { + JSONArray array = new JSONArray(); + JSONObject json = new JSONObject(request); + JSONArray languages = json.getJSONArray("languages"); + for (int i = 0; i < languages.length(); i++) { + String code = languages.getString(i); + JSONArray a = new JSONArray(); + a.put(code); + a.put(LanguageUtils.getLanguage(code)); + array.put(a); + } + result.put("languages", array); + } catch (IOException | SAXException | ParserConfigurationException e) { + logger.log(Level.ERROR, e); + result.put(Constants.REASON, e.getMessage()); + } + return result; + } + + private JSONObject toJSON(Element e) { + JSONObject result = new JSONObject(); + result.put("name", e.getName()); + JSONArray attributes = new JSONArray(); + List atts = e.getAttributes(); + for (int i = 0; i < atts.size(); i++) { + Attribute a = atts.get(i); + JSONArray o = new JSONArray(); + o.put(a.getName()); + o.put(a.getValue()); + attributes.put(o); + } + if (attributes.length() > 0) { + result.put("attributes", attributes); + } + JSONArray array = new JSONArray(); + List children = e.getChildren(); + for (int i = 0; i < children.size(); i++) { + array.put(toJSON(children.get(i))); + } + if (array.length() > 0) { + result.put("children", array); + } + String text = e.getText().trim(); + if (!text.isBlank()) { + result.put("content", text); + } + return result; + } } \ No newline at end of file diff --git a/src/com/maxprograms/swordfish/TmsServer.java b/src/com/maxprograms/swordfish/TmsServer.java index 7fe17c8..6d0ce88 100644 --- a/src/com/maxprograms/swordfish/TmsServer.java +++ b/src/com/maxprograms/swordfish/TmsServer.java @@ -27,6 +27,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.sql.SQLException; +import java.text.MessageFormat; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -54,7 +55,8 @@ public static void main(String[] args) { for (int i = 0; i < args.length; i++) { String arg = args[i]; if (arg.equals("-version")) { - logger.log(Level.INFO, () -> "Version: " + Constants.VERSION + " Build: " + Constants.BUILD); + MessageFormat mf = new MessageFormat(Messages.getString("TmsServer.0")); + logger.log(Level.INFO, () -> mf.format(new String[] { Constants.VERSION, Constants.BUILD })); return; } if (arg.equals("-port") && (i + 1) < args.length) { @@ -68,7 +70,7 @@ public static void main(String[] args) { TmsServer instance = new TmsServer(Integer.valueOf(port)); instance.run(); } catch (Exception e) { - logger.log(Level.ERROR, "Server error", e); + logger.log(Level.ERROR, Messages.getString("TmsServer.1"), e); } } @@ -81,7 +83,7 @@ private void run() { server.setExecutor(new ThreadPoolExecutor(4, 8, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<>(100))); server.start(); if (debug) { - logger.log(Level.INFO, "TMS server started"); + logger.log(Level.INFO, Messages.getString("TmsServer.2")); } } @@ -94,7 +96,7 @@ public void handle(HttpExchange t) throws IOException { request = readRequestBody(is); } if (request.isBlank()) { - throw new IOException("Empty request"); + throw new IOException(Messages.getString("TmsServer.3")); } if (debug) { logger.log(Level.INFO, request); @@ -112,7 +114,7 @@ public void handle(HttpExchange t) throws IOException { break; case "stop": if (debug) { - logger.log(Level.INFO, "Stopping server"); + logger.log(Level.INFO, Messages.getString("TmsServer.4")); } closeAll(); obj.put(Constants.STATUS, Constants.OK); @@ -120,7 +122,8 @@ public void handle(HttpExchange t) throws IOException { break; default: obj.put(Constants.STATUS, Constants.ERROR); - obj.put(Constants.REASON, "Unknown command"); + MessageFormat mf = new MessageFormat(Messages.getString("TmsServer.5")); + obj.put(Constants.REASON, mf.format(new String[] { command })); obj.put("received", json.toString()); response = obj.toString(); } diff --git a/src/com/maxprograms/swordfish/swordfish.properties b/src/com/maxprograms/swordfish/swordfish.properties new file mode 100644 index 0000000..09bd155 --- /dev/null +++ b/src/com/maxprograms/swordfish/swordfish.properties @@ -0,0 +1,85 @@ +DbUpgrade.0=No folder specified +DbUpgrade.1=Upgrading project {0} +DbUpgrade.2=Upgrading memory {0} +GlossariesHandler.0=Error processing glossary {0} +GlossariesHandler.1=Unknown request {0} +GlossariesHandler.10=Missing 'file' parameter +GlossariesHandler.11=Glossary file does not exist +GlossariesHandler.12=Imported: {0} +GlossariesHandler.13=Premature end of file +GlossariesHandler.14=Missing 'glossary' parameter +GlossariesHandler.2=Missing 'process' parameter +GlossariesHandler.3=No such process: {0} +GlossariesHandler.4=Missing 'glossaries' parameter +GlossariesHandler.5=Folder ''{0}'' will be deleted on next start +GlossariesHandler.6=Missing 'glossary' parameter +GlossariesHandler.7=Missing 'file' parameter +GlossariesHandler.8=Glossaries closed +GlossariesHandler.9=Missing 'glossary' parameter +MemoriesHandler.0=Error processing memory {0} +MemoriesHandler.1=Unknown request {0} +MemoriesHandler.10=Imported: {0} +MemoriesHandler.11=Missing 'memory' parameter +MemoriesHandler.12=Missing 'tmx' parameter +MemoriesHandler.13=Missing 'memories' parameter +MemoriesHandler.14=Folder ''{0}'' will be deleted on next start +MemoriesHandler.15=Memories closed +MemoriesHandler.2=Missing 'memory' parameter +MemoriesHandler.3=Missing 'process' parameter +MemoriesHandler.4=No such process: {0} +MemoriesHandler.5=Missing 'memories' parameter +MemoriesHandler.6=Invalid regular expression +MemoriesHandler.7=Missing 'memory' parameter +MemoriesHandler.8=Missing 'tmx' parameter +MemoriesHandler.9=TMX file does not exist +ProjectsHandler.0=Error processing project {0} +ProjectsHandler.1=Unknown request {0} +ProjectsHandler.10=Error retrieving count +ProjectsHandler.11=Conversion failed for: {0} +ProjectsHandler.12=Project list not loaded +ProjectsHandler.13=Projects map is null +ProjectsHandler.14=Project is not open +ProjectsHandler.15=no open projects +ProjectsHandler.16=Projects closed +ProjectsHandler.17=XLIFF file does not exist +ProjectsHandler.2=Project does not exist +ProjectsHandler.3=Error creating project store +ProjectsHandler.4=Error exporting translations +ProjectsHandler.5=Error creating project store +ProjectsHandler.6=Error exporting TMX +ProjectsHandler.7=Null project requested +ProjectsHandler.8=Store is null +ProjectsHandler.9=Error loading segments +ServicesHandler.0=Error processing service request {0} +ServicesHandler.1=Unknown request {0} +ServicesHandler.2=Incorrect file type +ServicesHandler.3=Error importing XML configuration +ServicesHandler.4=Configuration exists +ServicesHandler.5=Error getting languages +ServicesHandler.6={0} Build: {1} +ServicesHandler.7={0} Vendor: {1} +Subscriptions.11=Running on sandbox, evaluation request discarded +Subscriptions.12=Error generating evaluation key +Subscriptions.13=Cannot register evaluation in this computer +Subscriptions.17=Unsupported macOS version +Subscriptions.18=Unsupported Linux distribution +Subscriptions.19=Unsupported Windows version +Subscriptions.20=Unsupported computer type +Subscriptions.33=Error sending request to server. Check your firewall or antivirus. +Subscriptions.34=Error reading answer from server. Check your firewall or antivirus. +Subscriptions.37=Selected email account is not supported +Subscriptions.39=An evaluation subscription has been requested for this computer before +Subscriptions.40=Evaluation request failed +Subscriptions.43=Expired Subscription +Subscriptions.45=Unsupported hard disks +Subscriptions.56=Error registering subscription +Subscriptions.58=Error registering subscription +Subscriptions.6=No subscription +Subscriptions.7=Error verifying subscription status +Subscriptions.9=A computer user account is required +TmsServer.0=Version: {0} Build: {1} +TmsServer.1=Server error +TmsServer.2=TMS server started +TmsServer.3=Empty request +TmsServer.4=Stopping server +TmsServer.5=Unknown command {0} diff --git a/src/com/maxprograms/swordfish/tm/InternalDatabase.java b/src/com/maxprograms/swordfish/tm/InternalDatabase.java deleted file mode 100755 index 0b87d5f..0000000 --- a/src/com/maxprograms/swordfish/tm/InternalDatabase.java +++ /dev/null @@ -1,735 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007 - 2024 Maxprograms. - * - * This program and the accompanying materials - * are made available under the terms of the Eclipse Public License 1.0 - * which accompanies this distribution, and is available at - * https://www.eclipse.org/org/documents/epl-v10.html - * - * Contributors: - * Maxprograms - initial API and implementation - *******************************************************************************/ - -package com.maxprograms.swordfish.tm; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.StringReader; -import java.lang.System.Logger; -import java.lang.System.Logger.Level; -import java.nio.charset.StandardCharsets; -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.text.MessageFormat; -import java.util.Calendar; -import java.util.Collections; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.NavigableSet; -import java.util.Set; -import java.util.TreeSet; -import java.util.Vector; - -import javax.xml.parsers.ParserConfigurationException; - -import com.maxprograms.languages.LanguageUtils; -import com.maxprograms.swordfish.Constants; -import com.maxprograms.swordfish.MemoriesHandler; -import com.maxprograms.swordfish.TmsServer; -import com.maxprograms.swordfish.tmx.TMXReader; -import com.maxprograms.xml.Element; -import com.maxprograms.xml.Indenter; -import com.maxprograms.xml.XMLUtils; - -import org.json.JSONArray; -import org.json.JSONObject; -import org.mapdb.Fun; -import org.mapdb.Fun.Tuple2; -import org.xml.sax.SAXException; - -public class InternalDatabase implements ITmEngine { - - protected static final Logger logger = System.getLogger(InternalDatabase.class.getName()); - - private String dbname; - private String url; - private Connection conn; - private PreparedStatement storeTUV; - private PreparedStatement deleteTUV; - private PreparedStatement searchTUV; - private String currProject; - private String currSubject; - private String currCustomer; - private FileOutputStream output; - private String creationDate; - private FuzzyIndex fuzzyIndex; - private TuDatabase tuDb; - private File database; - private long next; - - public InternalDatabase(String dbname, String workFolder) throws SQLException, IOException { - this.dbname = dbname; - creationDate = TMUtils.tmxDate(); - - File wfolder = new File(workFolder); - database = new File(wfolder, dbname); - boolean exists = database.exists(); - if (!exists) { - database.mkdirs(); - } - url = "jdbc:h2:" + database.getAbsolutePath() + "/db;DB_CLOSE_ON_EXIT=FALSE"; - conn = DriverManager.getConnection(url); - - if (!exists) { - createTable(); - logger.log(Level.INFO, "H2 database created"); - } - - boolean needsUpgrade = false; - try (Statement stmt = conn.createStatement()) { - String sql = "SELECT TYPE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='TUV' AND COLUMN_NAME='SEG'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - needsUpgrade = !rs.getString(1).equalsIgnoreCase("CLOB"); - } - } - } - if (needsUpgrade) { - String s1 = "ALTER TABLE TUV ALTER COLUMN SEG SET DATA TYPE CLOB"; - String s2 = "ALTER TABLE TUV ALTER COLUMN PURETEXT SET DATA TYPE CLOB"; - try (Statement upgrade = conn.createStatement()) { - upgrade.execute(s1); - upgrade.execute(s2); - conn.commit(); - } - } - - storeTUV = conn.prepareStatement("INSERT INTO tuv (tuid, lang, seg, puretext, textlength) VALUES (?,?,?,?,?)"); - searchTUV = conn.prepareStatement("SELECT textlength FROM tuv WHERE tuid=? AND lang=?"); - deleteTUV = conn.prepareStatement("DELETE FROM tuv WHERE tuid=? AND lang=?"); - try { - tuDb = new TuDatabase(database); - } catch (Exception e) { - logger.log(Level.ERROR, e.getMessage(), e); - MessageFormat mf = new MessageFormat("TU storage of database {0} is damaged"); - throw new IOException(mf.format(new String[] { dbname })); - } - try { - fuzzyIndex = new FuzzyIndex(database); - } catch (Exception e) { - logger.log(Level.ERROR, e.getMessage(), e); - MessageFormat mf = new MessageFormat("Fuzzy index of database {0} is damaged"); - throw new IOException(mf.format(new String[] { dbname })); - } - } - - private void createTable() throws SQLException { - String sql = "CREATE TABLE tuv (tuid VARCHAR(256) NOT NULL, lang VARCHAR(15) NOT NULL, seg CLOB NOT NULL, puretext CLOB NOT NULL, textlength INTEGER NOT NULL, PRIMARY KEY(tuid, lang));"; - try (Statement stmt = conn.createStatement()) { - stmt.execute(sql); - } - conn.commit(); - } - - @Override - public synchronized void close() throws SQLException { - storeTUV.close(); - deleteTUV.close(); - searchTUV.close(); - conn.commit(); - conn.close(); - fuzzyIndex.commit(); - fuzzyIndex.close(); - tuDb.commit(); - tuDb.close(); - } - - @Override - public String getName() { - return dbname; - } - - private void startTransaction() throws SQLException { - conn.setAutoCommit(false); - } - - @Override - public synchronized void commit() throws SQLException { - conn.commit(); - fuzzyIndex.commit(); - tuDb.commit(); - } - - @Override - public int storeTMX(String tmxFile, String project, String customer, String subject) - throws SQLException, IOException, SAXException, ParserConfigurationException { - int imported = 0; - next = 0l; - if (customer == null) { - customer = ""; - } - if (subject == null) { - subject = ""; - } - if (project == null) { - project = ""; - } - currProject = project; - currSubject = subject; - currCustomer = customer; - creationDate = TMUtils.creationDate(); - - startTransaction(); - - TMXReader reader = new TMXReader(this); - reader.parse(new File(tmxFile).toURI().toURL()); - imported = reader.getCount(); - - commit(); - return imported; - } - - @Override - public void exportMemory(String tmxfile, Set langs, String srcLang) throws IOException, SQLException { - output = new FileOutputStream(tmxfile); - writeHeader(srcLang); - writeString("\n"); - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=?")) { - try (Statement tus = conn.createStatement()) { - try (ResultSet tuKeys = tus.executeQuery("SELECT DISTINCT TUID from TUV")) { - while (tuKeys.next()) { - String tuid = tuKeys.getString(1); - Element tu = tuDb.getTu(tuid); - stmt.setString(1, tuid); - int count = 0; - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - String lang = rs.getString(1); - String seg = TMUtils.getString(rs.getNCharacterStream(2)); - if (seg.equals("") || !langs.contains(lang)) { - continue; - } - try { - Element tuv = TMUtils.buildTuv(lang, seg); - tu.addContent(tuv); - count++; - } catch (Exception e) { - logger.log(Level.ERROR, "Error building tuv", e); - logger.log(Level.INFO, "seg: " + seg); - } - } - } - if (count >= 2) { - Indenter.indent(tu, 2); - writeString(tu.toString() + "\n"); - } - } - } - } - } - writeString("\n"); - writeString("\n"); - output.close(); - } - - private void writeString(String string) throws IOException { - output.write(string.getBytes(StandardCharsets.UTF_8)); - } - - private void writeHeader(String srcLang) throws IOException { - writeString("\n"); - writeString( - "\n"); - writeString("\n"); - writeString("
\n"); - } - - @Override - public Set getAllClients() { - return tuDb.getCustomers(); - } - - @Override - public Set getAllLanguages() throws SQLException { - Set result = Collections.synchronizedSortedSet(new TreeSet<>()); - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (Statement stmt = conn.createStatement()) { - try (ResultSet rs = stmt.executeQuery("SELECT DISTINCT lang FROM tuv")) { - while (rs.next()) { - result.add(rs.getString(1)); - } - } - } - return result; - } - - @Override - public Set getAllProjects() { - return tuDb.getProjects(); - } - - @Override - public Set getAllSubjects() { - return tuDb.getSubjects(); - } - - @Override - public List searchTranslation(String searchStr, String srcLang, String tgtLang, int similarity, - boolean caseSensitive) throws SAXException, IOException, ParserConfigurationException, SQLException { - // search for TUs with a given source and target language - List result = new Vector<>(); - - int[] ngrams = null; - ngrams = NGrams.getNGrams(searchStr); - int size = ngrams.length; - if (size == 0) { - return result; - } - int min = size * similarity / 100; - int max = size * (200 - similarity) / 100; - - int minLength = searchStr.length() * similarity / 100; - int maxLength = searchStr.length() * (200 - similarity) / 100; - - Hashtable candidates = new Hashtable<>(); - - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement( - "SELECT puretext, seg, textlength FROM tuv WHERE lang=? AND tuid=? AND textlength>=? AND textlength<=?")) { - stmt.setString(1, srcLang); - stmt.setInt(3, minLength); - stmt.setInt(4, maxLength); - - try (PreparedStatement stmt2 = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=? AND lang=?")) { - stmt2.setString(2, tgtLang); - - NavigableSet> index = fuzzyIndex.getIndex(srcLang); - for (int i = 0; i < ngrams.length; i++) { - Iterable keys = Fun.filter(index, ngrams[i]); - Iterator it = keys.iterator(); - while (it.hasNext()) { - String tuid = it.next(); - if (candidates.containsKey(tuid)) { - int count = candidates.get(tuid); - candidates.put(tuid, count + 1); - } else { - candidates.put(tuid, 1); - } - } - } - Enumeration tuids = candidates.keys(); - while (tuids.hasMoreElements()) { - String tuid = tuids.nextElement(); - int count = candidates.get(tuid); - if (count >= min && count <= max) { - stmt.setString(2, tuid); - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - String pure = TMUtils.getString(rs.getNCharacterStream(1)); - String srcSeg = TMUtils.getString(rs.getNCharacterStream(2)); - int distance; - if (caseSensitive) { - distance = MatchQuality.similarity(searchStr, pure); - } else { - distance = MatchQuality.similarity(searchStr.toLowerCase(), pure.toLowerCase()); - } - if (distance >= similarity) { - stmt2.setString(1, tuid); - stmt2.setString(2, tgtLang); - boolean tgtFound = false; - Element target = null; - try (ResultSet rs2 = stmt2.executeQuery()) { - while (rs2.next()) { - String lang = rs2.getString(1); - String seg = TMUtils.getString(rs2.getNCharacterStream(2)); - target = TMUtils.buildTuv(lang, seg); - tgtFound = true; - } - } - if (tgtFound) { - Element source = TMUtils.buildTuv(srcLang, srcSeg); - Map propsMap = new Hashtable<>(); - Element tu = getTu(tuid); - List props = tu.getChildren("prop"); - Iterator pt = props.iterator(); - while (pt.hasNext()) { - Element prop = pt.next(); - propsMap.put(prop.getAttributeValue("type"), prop.getText()); - } - Match match = new Match(source, target, distance, dbname, propsMap); - result.add(match); - } - } - } - } - } - } - } - } - Collections.sort(result); - return result; - } - - @Override - public List concordanceSearch(String searchStr, String srcLang, int limit, boolean isRegexp, - boolean caseSensitive) throws SQLException, SAXException, IOException, ParserConfigurationException { - List result = new Vector<>(); - Vector candidates = new Vector<>(); - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - if (isRegexp) { - try (PreparedStatement stmt = conn - .prepareStatement("SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext REGEXP ? LIMIT ?")) { - stmt.setString(1, srcLang); - stmt.setString(2, searchStr); - stmt.setInt(3, limit); - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - candidates.add(rs.getString(1)); - } - } - } - } else { - String sql = caseSensitive ? "SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext LIKE ? LIMIT ?" - : "SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext ILIKE ? LIMIT ?"; - String escaped = searchStr.replace("%", "\\%").replace("_", "\\_"); - try (PreparedStatement stmt = conn.prepareStatement(sql)) { - stmt.setString(1, srcLang); - stmt.setString(2, "%" + escaped + "%"); - stmt.setInt(3, limit); - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - candidates.add(rs.getString(1)); - } - } - } - } - - Iterator it = candidates.iterator(); - while (it.hasNext()) { - String tuid = it.next(); - Element tu = getTu(tuid); - result.add(tu); - } - return result; - } - - @Override - public synchronized void storeTu(Element tu) throws SQLException, IOException { - Set tuLangs = Collections.synchronizedSortedSet(new TreeSet<>()); - List tuvs = tu.getChildren("tuv"); - String tuid = tu.getAttributeValue("tuid"); - if (tuid.isEmpty()) { - tuid = nextId(); - tu.setAttribute("tuid", tuid); - } - - Hashtable props = new Hashtable<>(); - List properties = tu.getChildren("prop"); - Iterator kt = properties.iterator(); - while (kt.hasNext()) { - Element prop = kt.next(); - props.put(prop.getAttributeValue("type"), prop.getText()); - } - if (currSubject != null && !currSubject.isEmpty() && !props.containsKey("subject")) { - Element prop = new Element("prop"); - prop.setAttribute("type", "subject"); - prop.setText(XMLUtils.cleanText(currSubject)); - List content = tu.getChildren(); - content.add(0, prop); - tu.setChildren(content); - props.put(prop.getAttributeValue("type"), prop.getText()); - } - String sub = props.get("subject"); - if (sub != null) { - tuDb.storeSubject(sub); - } - if (currCustomer != null && !currCustomer.isEmpty() && !props.containsKey("customer")) { - Element prop = new Element("prop"); - prop.setAttribute("type", "customer"); - prop.setText(XMLUtils.cleanText(currCustomer)); - List content = tu.getChildren(); - content.add(0, prop); - tu.setChildren(content); - props.put(prop.getAttributeValue("type"), prop.getText()); - } - String cust = props.get("customer"); - if (cust != null) { - tuDb.storeCustomer(cust); - } - if (currProject != null && !currProject.isEmpty() && !props.containsKey("project")) { - Element prop = new Element("prop"); - prop.setAttribute("type", "project"); - prop.setText(XMLUtils.cleanText(currProject)); - List content = tu.getChildren(); - content.add(0, prop); - tu.setChildren(content); - props.put(prop.getAttributeValue("type"), prop.getText()); - } - String proj = props.get("project"); - if (proj != null) { - tuDb.storeProject(proj); - } - if (tu.getAttributeValue("creationdate").isEmpty()) { - tu.setAttribute("creationdate", creationDate); - } - if (tu.getAttributeValue("creationid").isEmpty()) { - tu.setAttribute("creationid", System.getProperty("user.name")); - } - - storeTUV.setString(1, tuid); - - Iterator it = tuvs.iterator(); - while (it.hasNext()) { - Element tuv = it.next(); - String lang = LanguageUtils.normalizeCode(tuv.getAttributeValue("xml:lang")); - if (lang != null && !tuLangs.contains(lang)) { - if (exists(tuid, lang)) { - delete(tuid, lang); - } - Element seg = tuv.getChild("seg"); - String puretext = TMUtils.extractText(seg); - if (puretext.length() < 1) { - continue; - } - storeTUV.setString(2, lang); - storeTUV.setNCharacterStream(3, new StringReader(seg.toString())); - storeTUV.setNCharacterStream(4, new StringReader(puretext)); - storeTUV.setInt(5, puretext.length()); - storeTUV.execute(); - tuLangs.add(lang); - - tuDb.store(tuid, tu); - - int[] ngrams = NGrams.getNGrams(puretext); - NavigableSet> index = fuzzyIndex.getIndex(lang); - for (int i = 0; i < ngrams.length; i++) { - Tuple2 entry = Fun.t2(ngrams[i], tuid); - if (!index.contains(entry)) { - index.add(entry); - } - } - } - } - } - - private String nextId() { - if (next == 0l) { - next = Calendar.getInstance().getTimeInMillis(); - } - return "" + next++; - } - - private boolean exists(String tuid, String lang) throws SQLException { - searchTUV.setString(1, tuid); - searchTUV.setString(2, lang); - boolean found = false; - try (ResultSet rs = searchTUV.executeQuery()) { - while (rs.next()) { - found = true; - } - } - return found; - } - - private void delete(String tuid, String lang) throws SQLException { - deleteTUV.setString(1, tuid); - deleteTUV.setString(2, lang); - deleteTUV.execute(); - } - - public void setProject(String project) throws SQLException { - String query = "UPDATE databases SET project=? WHERE dbname=?"; - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement(query)) { - stmt.setString(1, project); - stmt.setString(2, dbname); - stmt.execute(); - } - } - - public void setCustomer(String customer) throws SQLException { - String query = "UPDATE databases SET client=? WHERE dbname=?"; - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement(query)) { - stmt.setString(1, customer); - stmt.setString(2, dbname); - stmt.execute(); - } - } - - public void setSubject(String subject) throws SQLException { - String query = "UPDATE databases SET subject=? WHERE dbname=?"; - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement(query)) { - stmt.setString(1, subject); - stmt.setString(2, dbname); - stmt.execute(); - } - } - - public void setCreationDate(String date) { - creationDate = date; - } - - @Override - public Element getTu(String tuid) throws SQLException, SAXException, IOException, ParserConfigurationException { - Element tu = tuDb.getTu(tuid); - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=?")) { - stmt.setString(1, tuid); - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - String lang = rs.getString(1); - String seg = TMUtils.getString(rs.getNCharacterStream(2)); - if (seg.equals("")) { - continue; - } - Element tuv = TMUtils.buildTuv(lang, seg); - tu.addContent(tuv); - } - } - } - return tu; - } - - @Override - public void removeTu(String tuid) throws IOException, SQLException, SAXException, ParserConfigurationException { - Element tu = getTu(tuid); - List tuvs = tu.getChildren("tuv"); - Iterator it = tuvs.iterator(); - while (it.hasNext()) { - Element tuv = it.next(); - String lang = LanguageUtils.normalizeCode(tuv.getAttributeValue("xml:lang")); - delete(tuid, lang); - } - tuDb.remove(tuid); - commit(); - } - - @Override - public String getType() { - return InternalDatabase.class.getName(); - } - - @Override - public void deleteDatabase() throws IOException, SQLException { - TmsServer.deleteFolder(new File(MemoriesHandler.getWorkFolder(), dbname)); - } - - @Override - public List searchAll(String searchStr, String srcLang, int similarity, boolean caseSensitive) - throws IOException, SAXException, ParserConfigurationException, SQLException { - List result = new Vector<>(); - - int[] ngrams = NGrams.getNGrams(searchStr); - int size = ngrams.length; - if (size == 0) { - return result; - } - int min = size * similarity / 100; - int max = size * (200 - similarity) / 100; - - int minLength = searchStr.length() * similarity / 100; - int maxLength = searchStr.length() * (200 - similarity) / 100; - - Map candidates = new Hashtable<>(); - if (conn.isClosed()) { - conn = DriverManager.getConnection(url); - } - try (PreparedStatement stmt = conn.prepareStatement( - "SELECT puretext FROM tuv WHERE lang=? AND tuid=? AND textlength>=? AND textlength<=?")) { - stmt.setString(1, srcLang); - stmt.setInt(3, minLength); - stmt.setInt(4, maxLength); - - NavigableSet> index = fuzzyIndex.getIndex(srcLang); - for (int i = 0; i < ngrams.length; i++) { - Iterable keys = Fun.filter(index, ngrams[i]); - Iterator it = keys.iterator(); - while (it.hasNext()) { - String tuid = it.next(); - if (candidates.containsKey(tuid)) { - int count = candidates.get(tuid); - candidates.put(tuid, count + 1); - } else { - candidates.put(tuid, 1); - } - } - } - Set tuids = candidates.keySet(); - Iterator it = tuids.iterator(); - while (it.hasNext()) { - String tuid = it.next(); - int count = candidates.get(tuid); - if (count >= min && count <= max) { - stmt.setString(2, tuid); - try (ResultSet rs = stmt.executeQuery()) { - while (rs.next()) { - String pure = TMUtils.getString(rs.getNCharacterStream(1)); - int distance; - if (caseSensitive) { - distance = MatchQuality.similarity(searchStr, pure); - } else { - distance = MatchQuality.similarity(searchStr.toLowerCase(), pure.toLowerCase()); - } - if (distance >= similarity) { - Element tu = getTu(tuid); - result.add(tu); - } - } - } - } - } - } - return result; - } - - @Override - public JSONArray batchTranslate(JSONObject params) - throws IOException, SAXException, ParserConfigurationException, SQLException { - JSONArray result = new JSONArray(); - String srcLang = params.getString("srcLang"); - String tgtLang = params.getString("tgtLang"); - JSONArray segments = params.getJSONArray("segments"); - boolean caseSensitiveMatches = params.getBoolean("caseSensitiveMatches"); - for (int i = 0; i < segments.length(); i++) { - JSONObject json = segments.getJSONObject(i); - List matches = searchTranslation(json.getString("pure"), srcLang, tgtLang, 60, caseSensitiveMatches); - JSONArray array = new JSONArray(); - for (int j = 0; j < matches.size(); j++) { - array.put(matches.get(j).toJSON()); - } - json.put("matches", array); - result.put(json); - } - return result; - } -} diff --git a/src/com/maxprograms/swordfish/tm/Messages.java b/src/com/maxprograms/swordfish/tm/Messages.java new file mode 100644 index 0000000..b1a77b0 --- /dev/null +++ b/src/com/maxprograms/swordfish/tm/Messages.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +package com.maxprograms.swordfish.tm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Properties; + +public class Messages { + + private static Properties props; + + private Messages() { + // do not instantiate this class + } + + public static String getString(String key) { + String resourceName = "tm"; + try { + if (props == null) { + Locale locale = Locale.getDefault(); + String language = locale.getLanguage(); + String extension = "_" + language + ".properties"; + // check if there is a resource for full language code + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, check if there is a resource for language only + extension = "_" + language.substring(0, 2) + ".properties"; + } + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, use the default resource + extension = ".properties"; + } + try (InputStream is = Messages.class.getResourceAsStream(resourceName + extension)) { + try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + props = new Properties(); + props.load(reader); + } + } + } + return props.getProperty(key, '!' + key + '!'); + } catch (IOException | NullPointerException e) { + return '!' + key + '!'; + } + } +} diff --git a/src/com/maxprograms/swordfish/tm/RemoteDatabase.java b/src/com/maxprograms/swordfish/tm/RemoteDatabase.java index 12f6368..6ea5c50 100644 --- a/src/com/maxprograms/swordfish/tm/RemoteDatabase.java +++ b/src/com/maxprograms/swordfish/tm/RemoteDatabase.java @@ -199,8 +199,10 @@ private String upload(File zipFile) throws IOException, URISyntaxException { try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { String line; while ((line = reader.readLine()) != null) { + if (!sb.isEmpty()) { + sb.append('\n'); + } sb.append(line); - sb.append('\n'); } } } diff --git a/src/com/maxprograms/swordfish/tm/SqliteDatabase.java b/src/com/maxprograms/swordfish/tm/SqliteDatabase.java new file mode 100644 index 0000000..58bd32c --- /dev/null +++ b/src/com/maxprograms/swordfish/tm/SqliteDatabase.java @@ -0,0 +1,681 @@ +/******************************************************************************* + * Copyright (c) 2008-2023 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ +package com.maxprograms.swordfish.tm; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.System.Logger; +import java.lang.System.Logger.Level; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; +import java.util.Vector; +import java.util.regex.Pattern; + +import javax.xml.parsers.ParserConfigurationException; + +import org.json.JSONArray; +import org.json.JSONObject; +import org.mapdb.Fun; +import org.mapdb.Fun.Tuple2; +import org.sqlite.Function; +import org.xml.sax.SAXException; + +import com.maxprograms.languages.LanguageUtils; +import com.maxprograms.swordfish.Constants; +import com.maxprograms.swordfish.TmsServer; +import com.maxprograms.swordfish.tmx.TMXReader; +import com.maxprograms.xml.Element; +import com.maxprograms.xml.Indenter; +import com.maxprograms.xml.XMLUtils; + +public class SqliteDatabase implements ITmEngine { + + Logger logger = System.getLogger(SqliteDatabase.class.getName()); + + private String dbname; + private String creationDate; + private File databaseFolder; + private File database; + private Connection conn; + private PreparedStatement storeTUV; + private PreparedStatement searchTUV; + private PreparedStatement deleteTUV; + private TuDatabase tuDb; + private FuzzyIndex fuzzyIndex; + private long next; + private String currProject; + private String currSubject; + private String currCustomer; + private FileOutputStream output; + + private TMXReader reader; + + public SqliteDatabase(String dbname, String workFolder) throws IOException, SQLException { + this.dbname = dbname; + creationDate = TMUtils.tmxDate(); + + File wfolder = new File(workFolder); + databaseFolder = new File(wfolder, dbname); + if (!databaseFolder.exists()) { + Files.createDirectories(databaseFolder.toPath()); + } + if (new File(databaseFolder, "db.mv.db").exists()) { + MessageFormat mf = new MessageFormat(Messages.getString("SqliteDatabase.0")); + throw new IOException(mf.format(new String[] { databaseFolder.getName() })); + } + database = new File(databaseFolder, "database.db"); + boolean sqliteNeedsCreation = !database.exists(); + DriverManager.registerDriver(new org.sqlite.JDBC()); + conn = DriverManager.getConnection("jdbc:sqlite:" + database.getAbsolutePath().replace('\\', '/')); + conn.setAutoCommit(false); + Function.create(conn, "REGEXP", new Function() { + @Override + protected void xFunc() throws SQLException { + String expression = value_text(0); + String value = value_text(1); + if (value == null) + value = ""; + + Pattern pattern = Pattern.compile(expression); + result(pattern.matcher(value).find() ? 1 : 0); + } + }); + if (sqliteNeedsCreation) { + createTables(); + } + storeTUV = conn.prepareStatement("INSERT INTO tuv (tuid, lang, seg, puretext, textlength) VALUES (?,?,?,?,?)"); + searchTUV = conn.prepareStatement("SELECT textlength FROM tuv WHERE tuid=? AND lang=?"); + deleteTUV = conn.prepareStatement("DELETE FROM tuv WHERE tuid=? AND lang=?"); + try { + tuDb = new TuDatabase(databaseFolder); + } catch (Exception e) { + logger.log(Level.ERROR, e.getMessage(), e); + MessageFormat mf = new MessageFormat(Messages.getString("SqliteDatabase.1")); + throw new IOException(mf.format(new String[] { dbname })); + } + try { + fuzzyIndex = new FuzzyIndex(databaseFolder); + } catch (Exception e) { + logger.log(Level.ERROR, e.getMessage(), e); + MessageFormat mf = new MessageFormat(Messages.getString("SqliteDatabase.2")); + throw new IOException(mf.format(new String[] { dbname })); + } + } + + private void createTables() throws SQLException { + String sql = """ + CREATE TABLE tuv ( + tuid VARCHAR(256) NOT NULL, + lang VARCHAR(15) NOT NULL, + seg TEXT NOT NULL, + puretext TEXT NOT NULL, + textlength INTEGER NOT NULL, + PRIMARY KEY(tuid, lang) + );"""; + try (Statement stmt = conn.createStatement()) { + stmt.execute(sql); + } + conn.commit(); + } + + @Override + public JSONArray batchTranslate(JSONObject params) + throws IOException, SAXException, ParserConfigurationException, SQLException, URISyntaxException { + JSONArray result = new JSONArray(); + String srcLang = params.getString("srcLang"); + String tgtLang = params.getString("tgtLang"); + JSONArray segments = params.getJSONArray("segments"); + boolean caseSensitiveMatches = params.getBoolean("caseSensitiveMatches"); + for (int i = 0; i < segments.length(); i++) { + JSONObject json = segments.getJSONObject(i); + List matches = searchTranslation(json.getString("pure"), srcLang, tgtLang, 60, caseSensitiveMatches); + JSONArray array = new JSONArray(); + for (int j = 0; j < matches.size(); j++) { + array.put(matches.get(j).toJSON()); + } + json.put("matches", array); + result.put(json); + } + return result; + } + + @Override + public void close() throws IOException, SQLException, URISyntaxException { + storeTUV.close(); + deleteTUV.close(); + searchTUV.close(); + conn.commit(); + conn.close(); + fuzzyIndex.commit(); + fuzzyIndex.close(); + tuDb.commit(); + tuDb.close(); + } + + @Override + public void commit() throws SQLException, IOException, URISyntaxException { + conn.commit(); + fuzzyIndex.commit(); + tuDb.commit(); + } + + @Override + public List concordanceSearch(String searchStr, String srcLang, int limit, boolean isRegexp, + boolean caseSensitive) throws SQLException, SAXException, IOException, ParserConfigurationException { + List result = new Vector<>(); + Vector candidates = new Vector<>(); + if (isRegexp) { + try (PreparedStatement stmt = conn + .prepareStatement("SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext REGEXP ? LIMIT ?")) { + stmt.setString(1, srcLang); + stmt.setString(2, searchStr); + stmt.setInt(3, limit); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + candidates.add(rs.getString(1)); + } + } + } + } else { + String sql = caseSensitive ? "SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext GLOB ? LIMIT ?" + : "SELECT tuid, puretext FROM tuv WHERE lang=? AND puretext LIKE ? LIMIT ?"; + String escaped = searchStr.replace("%", "\\%").replace("_", "\\_"); + try (PreparedStatement stmt = conn.prepareStatement(sql)) { + stmt.setString(1, srcLang); + stmt.setString(2, caseSensitive ? "*" + escaped + "*" : "%" + escaped + "%"); + stmt.setInt(3, limit); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + candidates.add(rs.getString(1)); + } + } + } + } + + Iterator it = candidates.iterator(); + while (it.hasNext()) { + String tuid = it.next(); + Element tu = getTu(tuid); + result.add(tu); + } + return result; + } + + @Override + public void deleteDatabase() throws IOException { + TmsServer.deleteFolder(databaseFolder); + } + + private void writeString(String string) throws IOException { + output.write(string.getBytes(StandardCharsets.UTF_8)); + } + + private void writeHeader(String srcLang) throws IOException { + writeString("\n"); + writeString( + "\n"); + writeString("\n"); + writeString("
\n"); + } + + @Override + public void exportMemory(String tmxfile, Set langs, String srcLang) throws IOException, SQLException { + output = new FileOutputStream(tmxfile); + writeHeader(srcLang); + writeString("\n"); + try (PreparedStatement stmt = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=?")) { + try (Statement tus = conn.createStatement()) { + try (ResultSet tuKeys = tus.executeQuery("SELECT DISTINCT TUID from TUV")) { + while (tuKeys.next()) { + String tuid = tuKeys.getString(1); + Element tu = tuDb.getTu(tuid); + stmt.setString(1, tuid); + int count = 0; + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + String lang = rs.getString(1); + String seg = rs.getString(2); + if (seg.equals("") || !langs.contains(lang)) { + continue; + } + try { + Element tuv = TMUtils.buildTuv(lang, seg); + tu.addContent(tuv); + count++; + } catch (Exception e) { + logger.log(Level.ERROR, Messages.getString("SqliteDatabase.3"), e); + logger.log(Level.INFO, "seg: " + seg); + } + } + } + if (count >= 2) { + Indenter.indent(tu, 2); + writeString(tu.toString() + "\n"); + } + } + } + } + } + writeString("\n"); + writeString("\n"); + output.close(); + } + + @Override + public Set getAllClients() throws SQLException, IOException, URISyntaxException { + return tuDb.getCustomers(); + } + + @Override + public Set getAllLanguages() throws SQLException, IOException, URISyntaxException { + Set result = Collections.synchronizedSortedSet(new TreeSet<>()); + try (Statement stmt = conn.createStatement()) { + try (ResultSet rs = stmt.executeQuery("SELECT DISTINCT lang FROM tuv")) { + while (rs.next()) { + result.add(rs.getString(1)); + } + } + } + return result; + } + + @Override + public Set getAllProjects() throws SQLException, IOException, URISyntaxException { + return tuDb.getProjects(); + } + + @Override + public Set getAllSubjects() throws SQLException, IOException, URISyntaxException { + return tuDb.getSubjects(); + } + + @Override + public String getName() { + return dbname; + } + + @Override + public Element getTu(String tuid) + throws IOException, SAXException, ParserConfigurationException, SQLException { + Element tu = tuDb.getTu(tuid); + try (PreparedStatement stmt = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=?")) { + stmt.setString(1, tuid); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + String lang = rs.getString(1); + String seg = rs.getString(2); + if (seg.equals("")) { + continue; + } + Element tuv = TMUtils.buildTuv(lang, seg); + tu.addContent(tuv); + } + } + } + return tu; + } + + @Override + public String getType() { + return SqliteDatabase.class.getName(); + } + + @Override + public void removeTu(String tuid) + throws IOException, SAXException, ParserConfigurationException, SQLException, URISyntaxException { + Element tu = getTu(tuid); + List tuvs = tu.getChildren("tuv"); + Iterator it = tuvs.iterator(); + while (it.hasNext()) { + Element tuv = it.next(); + String lang = LanguageUtils.normalizeCode(tuv.getAttributeValue("xml:lang")); + delete(tuid, lang); + } + tuDb.remove(tuid); + commit(); + } + + private void delete(String tuid, String lang) throws SQLException { + deleteTUV.setString(1, tuid); + deleteTUV.setString(2, lang); + deleteTUV.execute(); + } + + @Override + public List searchAll(String searchStr, String srcLang, int similarity, boolean caseSensitive) + throws IOException, SAXException, ParserConfigurationException, SQLException { + List result = new Vector<>(); + + int[] ngrams = NGrams.getNGrams(searchStr); + int size = ngrams.length; + if (size == 0) { + return result; + } + int min = size * similarity / 100; + int max = size * (200 - similarity) / 100; + + int minLength = searchStr.length() * similarity / 100; + int maxLength = searchStr.length() * (200 - similarity) / 100; + + Map candidates = new Hashtable<>(); + try (PreparedStatement stmt = conn.prepareStatement( + "SELECT puretext FROM tuv WHERE lang=? AND tuid=? AND textlength>=? AND textlength<=?")) { + stmt.setString(1, srcLang); + stmt.setInt(3, minLength); + stmt.setInt(4, maxLength); + + NavigableSet> index = fuzzyIndex.getIndex(srcLang); + for (int i = 0; i < ngrams.length; i++) { + Iterable keys = Fun.filter(index, ngrams[i]); + Iterator it = keys.iterator(); + while (it.hasNext()) { + String tuid = it.next(); + if (candidates.containsKey(tuid)) { + int count = candidates.get(tuid); + candidates.put(tuid, count + 1); + } else { + candidates.put(tuid, 1); + } + } + } + Set tuids = candidates.keySet(); + Iterator it = tuids.iterator(); + while (it.hasNext()) { + String tuid = it.next(); + int count = candidates.get(tuid); + if (count >= min && count <= max) { + stmt.setString(2, tuid); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + String pure = rs.getString(1); + int distance; + if (caseSensitive) { + distance = MatchQuality.similarity(searchStr, pure); + } else { + distance = MatchQuality.similarity(searchStr.toLowerCase(), pure.toLowerCase()); + } + if (distance >= similarity) { + Element tu = getTu(tuid); + result.add(tu); + } + } + } + } + } + } + return result; + } + + @Override + public List searchTranslation(String searchStr, String srcLang, String tgtLang, int similarity, + boolean caseSensitive) throws SAXException, IOException, ParserConfigurationException, SQLException { + // search for TUs with a given source and target language + List result = new Vector<>(); + + int[] ngrams = null; + ngrams = NGrams.getNGrams(searchStr); + int size = ngrams.length; + if (size == 0) { + return result; + } + int min = size * similarity / 100; + int max = size * (200 - similarity) / 100; + + int minLength = searchStr.length() * similarity / 100; + int maxLength = searchStr.length() * (200 - similarity) / 100; + + Hashtable candidates = new Hashtable<>(); + try (PreparedStatement stmt = conn.prepareStatement( + "SELECT puretext, seg, textlength FROM tuv WHERE lang=? AND tuid=? AND textlength>=? AND textlength<=?")) { + stmt.setString(1, srcLang); + stmt.setInt(3, minLength); + stmt.setInt(4, maxLength); + + try (PreparedStatement stmt2 = conn.prepareStatement("SELECT lang, seg FROM tuv WHERE tuid=? AND lang=?")) { + stmt2.setString(2, tgtLang); + + NavigableSet> index = fuzzyIndex.getIndex(srcLang); + for (int i = 0; i < ngrams.length; i++) { + Iterable keys = Fun.filter(index, ngrams[i]); + Iterator it = keys.iterator(); + while (it.hasNext()) { + String tuid = it.next(); + if (candidates.containsKey(tuid)) { + int count = candidates.get(tuid); + candidates.put(tuid, count + 1); + } else { + candidates.put(tuid, 1); + } + } + } + Enumeration tuids = candidates.keys(); + while (tuids.hasMoreElements()) { + String tuid = tuids.nextElement(); + int count = candidates.get(tuid); + if (count >= min && count <= max) { + stmt.setString(2, tuid); + try (ResultSet rs = stmt.executeQuery()) { + while (rs.next()) { + String pure = rs.getString(1); + String srcSeg = rs.getString(2); + int distance; + if (caseSensitive) { + distance = MatchQuality.similarity(searchStr, pure); + } else { + distance = MatchQuality.similarity(searchStr.toLowerCase(), pure.toLowerCase()); + } + if (distance >= similarity) { + stmt2.setString(1, tuid); + stmt2.setString(2, tgtLang); + boolean tgtFound = false; + Element target = null; + try (ResultSet rs2 = stmt2.executeQuery()) { + while (rs2.next()) { + String lang = rs2.getString(1); + String seg = rs2.getString(2); + target = TMUtils.buildTuv(lang, seg); + tgtFound = true; + } + } + if (tgtFound) { + Element source = TMUtils.buildTuv(srcLang, srcSeg); + Map propsMap = new Hashtable<>(); + Element tu = getTu(tuid); + List props = tu.getChildren("prop"); + Iterator pt = props.iterator(); + while (pt.hasNext()) { + Element prop = pt.next(); + propsMap.put(prop.getAttributeValue("type"), prop.getText()); + } + Match match = new Match(source, target, distance, dbname, propsMap); + result.add(match); + } + } + } + } + } + } + } + } + Collections.sort(result); + return result; + } + + @Override + public int storeTMX(String tmxFile, String project, String customer, String subject) + throws SAXException, IOException, ParserConfigurationException, SQLException, URISyntaxException { + int imported = 0; + next = 0l; + if (customer == null) { + customer = ""; + } + if (subject == null) { + subject = ""; + } + if (project == null) { + project = ""; + } + currProject = project; + currSubject = subject; + currCustomer = customer; + creationDate = TMUtils.creationDate(); + + reader = new TMXReader(this); + reader.parse(new File(tmxFile).toURI().toURL()); + imported = reader.getCount(); + commit(); + return imported; + } + + @Override + public void storeTu(Element tu) throws IOException, SQLException, URISyntaxException { + Set tuLangs = Collections.synchronizedSortedSet(new TreeSet<>()); + List tuvs = tu.getChildren("tuv"); + String tuid = tu.getAttributeValue("tuid"); + if (tuid.isEmpty()) { + tuid = nextId(); + tu.setAttribute("tuid", tuid); + } + + Hashtable props = new Hashtable<>(); + List properties = tu.getChildren("prop"); + Iterator kt = properties.iterator(); + while (kt.hasNext()) { + Element prop = kt.next(); + props.put(prop.getAttributeValue("type"), prop.getText()); + } + if (currSubject != null && !currSubject.isEmpty() && !props.containsKey("subject")) { + Element prop = new Element("prop"); + prop.setAttribute("type", "subject"); + prop.setText(XMLUtils.cleanText(currSubject)); + List content = tu.getChildren(); + content.add(0, prop); + tu.setChildren(content); + props.put(prop.getAttributeValue("type"), prop.getText()); + } + String sub = props.get("subject"); + if (sub != null) { + tuDb.storeSubject(sub); + } + if (currCustomer != null && !currCustomer.isEmpty() && !props.containsKey("customer")) { + Element prop = new Element("prop"); + prop.setAttribute("type", "customer"); + prop.setText(XMLUtils.cleanText(currCustomer)); + List content = tu.getChildren(); + content.add(0, prop); + tu.setChildren(content); + props.put(prop.getAttributeValue("type"), prop.getText()); + } + String cust = props.get("customer"); + if (cust != null) { + tuDb.storeCustomer(cust); + } + if (currProject != null && !currProject.isEmpty() && !props.containsKey("project")) { + Element prop = new Element("prop"); + prop.setAttribute("type", "project"); + prop.setText(XMLUtils.cleanText(currProject)); + List content = tu.getChildren(); + content.add(0, prop); + tu.setChildren(content); + props.put(prop.getAttributeValue("type"), prop.getText()); + } + String proj = props.get("project"); + if (proj != null) { + tuDb.storeProject(proj); + } + if (tu.getAttributeValue("creationdate").isEmpty()) { + tu.setAttribute("creationdate", creationDate); + } + + storeTUV.setString(1, tuid); + + Iterator it = tuvs.iterator(); + while (it.hasNext()) { + Element tuv = it.next(); + String lang = LanguageUtils.normalizeCode(tuv.getAttributeValue("xml:lang")); + if (lang != null && !tuLangs.contains(lang)) { + if (exists(tuid, lang)) { + delete(tuid, lang); + } + Element seg = tuv.getChild("seg"); + String puretext = TMUtils.extractText(seg); + if (puretext.length() < 1) { + continue; + } + storeTUV.setString(2, lang); + storeTUV.setString(3, seg.toString()); + storeTUV.setString(4, puretext); + storeTUV.setInt(5, puretext.length()); + storeTUV.execute(); + tuLangs.add(lang); + + tuDb.store(tuid, tu); + + int[] ngrams = NGrams.getNGrams(puretext); + NavigableSet> index = fuzzyIndex.getIndex(lang); + for (int i = 0; i < ngrams.length; i++) { + Tuple2 entry = Fun.t2(ngrams[i], tuid); + if (!index.contains(entry)) { + index.add(entry); + } + } + } + } + } + + private String nextId() { + if (next == 0l) { + next = System.currentTimeMillis(); + } + return "" + next++; + } + + private boolean exists(String tuid, String lang) throws SQLException { + searchTUV.setString(1, tuid); + searchTUV.setString(2, lang); + boolean found = false; + try (ResultSet rs = searchTUV.executeQuery()) { + while (rs.next()) { + found = true; + } + } + return found; + } + + public int getCount() { + if (reader != null) { + return reader.getCount(); + } + return 0; + } +} diff --git a/src/com/maxprograms/swordfish/tm/TMUtils.java b/src/com/maxprograms/swordfish/tm/TMUtils.java index 6e4cb8f..1d02eff 100644 --- a/src/com/maxprograms/swordfish/tm/TMUtils.java +++ b/src/com/maxprograms/swordfish/tm/TMUtils.java @@ -18,6 +18,7 @@ import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.nio.charset.StandardCharsets; +import java.text.MessageFormat; import java.util.Calendar; import java.util.Iterator; import java.util.List; @@ -26,13 +27,13 @@ import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; + import com.maxprograms.xml.Element; import com.maxprograms.xml.SAXBuilder; import com.maxprograms.xml.TextNode; import com.maxprograms.xml.XMLNode; -import org.xml.sax.SAXException; - public class TMUtils { private static Logger logger = System.getLogger(TMUtils.class.getName()); @@ -94,7 +95,8 @@ public static long getGMTtime(String tmxDate) { calendar.set(year, month, date, hour, minute, second); return calendar.getTimeInMillis(); } catch (NumberFormatException e) { - logger.log(Level.WARNING, "Unsupported TMX date: " + tmxDate); + MessageFormat mf = new MessageFormat(Messages.getString("TMUtils.0")); + logger.log(Level.WARNING, mf.format(new String[] { tmxDate })); return 0l; } } @@ -179,13 +181,13 @@ public static String extractText(Element seg) { } public static String getString(Reader reader) throws IOException { - StringBuilder sb = new StringBuilder(); - char[] array = new char[1024]; - int read = 0; - while ((read = reader.read(array)) != -1) { - sb.append(array, 0, read); - } - reader.close(); - return sb.toString(); - } + StringBuilder sb = new StringBuilder(); + char[] array = new char[1024]; + int read = 0; + while ((read = reader.read(array)) != -1) { + sb.append(array, 0, read); + } + reader.close(); + return sb.toString(); + } } diff --git a/src/com/maxprograms/swordfish/tm/tm.properties b/src/com/maxprograms/swordfish/tm/tm.properties new file mode 100644 index 0000000..451c76e --- /dev/null +++ b/src/com/maxprograms/swordfish/tm/tm.properties @@ -0,0 +1,5 @@ +SqliteDatabase.0=Database {0} needs upgrade +SqliteDatabase.1=TU storage of database {0} is damaged +SqliteDatabase.2=Fuzzy index of database {0} is damaged +SqliteDatabase.3=Error building tuv +TMUtils.0=Unsupported TMX date: {0} diff --git a/src/com/maxprograms/swordfish/tmx/Messages.java b/src/com/maxprograms/swordfish/tmx/Messages.java new file mode 100644 index 0000000..de4ae7f --- /dev/null +++ b/src/com/maxprograms/swordfish/tmx/Messages.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +package com.maxprograms.swordfish.tmx; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Properties; + +public class Messages { + + private static Properties props; + + private Messages() { + // do not instantiate this class + } + + public static String getString(String key) { + String resourceName = "tmx"; + try { + if (props == null) { + Locale locale = Locale.getDefault(); + String language = locale.getLanguage(); + String extension = "_" + language + ".properties"; + // check if there is a resource for full language code + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, check if there is a resource for language only + extension = "_" + language.substring(0, 2) + ".properties"; + } + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, use the default resource + extension = ".properties"; + } + try (InputStream is = Messages.class.getResourceAsStream(resourceName + extension)) { + try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + props = new Properties(); + props.load(reader); + } + } + } + return props.getProperty(key, '!' + key + '!'); + } catch (IOException | NullPointerException e) { + return '!' + key + '!'; + } + } +} diff --git a/src/com/maxprograms/swordfish/tmx/TMXContentHandler.java b/src/com/maxprograms/swordfish/tmx/TMXContentHandler.java index 940bf7c..e235a94 100644 --- a/src/com/maxprograms/swordfish/tmx/TMXContentHandler.java +++ b/src/com/maxprograms/swordfish/tmx/TMXContentHandler.java @@ -17,6 +17,7 @@ import java.lang.System.Logger.Level; import java.net.URISyntaxException; import java.sql.SQLException; +import java.text.MessageFormat; import java.util.Deque; import java.util.concurrent.ConcurrentLinkedDeque; @@ -39,7 +40,7 @@ class TMXContentHandler implements IContentHandler { private boolean inCDATA = false; private int count; private ITmEngine db; - + public TMXContentHandler(ITmEngine tmEngine) { db = tmEngine; stack = new ConcurrentLinkedDeque<>(); @@ -67,7 +68,8 @@ public void endElement(String uri, String localName, String qName) throws SAXExc } } catch (IOException | SQLException | URISyntaxException e) { // ignore - logger.log(Level.WARNING, "Error storing " + current, e); + MessageFormat mf = new MessageFormat(Messages.getString("TMXContentHandler.0")); + logger.log(Level.WARNING, mf.format(new String[] { current.toString() }), e); } count++; current = null; diff --git a/src/com/maxprograms/swordfish/tmx/tmx.properties b/src/com/maxprograms/swordfish/tmx/tmx.properties new file mode 100644 index 0000000..7e18e3f --- /dev/null +++ b/src/com/maxprograms/swordfish/tmx/tmx.properties @@ -0,0 +1 @@ +TMXContentHandler.0=Error storing {0} diff --git a/src/com/maxprograms/swordfish/xliff/Messages.java b/src/com/maxprograms/swordfish/xliff/Messages.java new file mode 100644 index 0000000..66ef39b --- /dev/null +++ b/src/com/maxprograms/swordfish/xliff/Messages.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2007 - 2024 Maxprograms. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 1.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/org/documents/epl-v10.html + * + * Contributors: + * Maxprograms - initial API and implementation + *******************************************************************************/ + +package com.maxprograms.swordfish.xliff; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import java.util.Properties; + +public class Messages { + + private static Properties props; + + private Messages() { + // do not instantiate this class + } + + public static String getString(String key) { + String resourceName = "xliff"; + try { + if (props == null) { + Locale locale = Locale.getDefault(); + String language = locale.getLanguage(); + String extension = "_" + language + ".properties"; + // check if there is a resource for full language code + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, check if there is a resource for language only + extension = "_" + language.substring(0, 2) + ".properties"; + } + if (Messages.class.getResource(resourceName + extension) == null) { + // if not, use the default resource + extension = ".properties"; + } + try (InputStream is = Messages.class.getResourceAsStream(resourceName + extension)) { + try (InputStreamReader reader = new InputStreamReader(is, StandardCharsets.UTF_8)) { + props = new Properties(); + props.load(reader); + } + } + } + return props.getProperty(key, '!' + key + '!'); + } catch (IOException | NullPointerException e) { + return '!' + key + '!'; + } + } +} diff --git a/src/com/maxprograms/swordfish/xliff/Split.java b/src/com/maxprograms/swordfish/xliff/Split.java index 7845c54..1d5eb74 100644 --- a/src/com/maxprograms/swordfish/xliff/Split.java +++ b/src/com/maxprograms/swordfish/xliff/Split.java @@ -48,7 +48,7 @@ public static List split(String xliff, String outputFolder) Document doc = builder.build(xliff); Element root = doc.getRootElement(); if (!"xliff".equals(root.getName())) { - throw new IOException("Selected file is not an XLIFF document"); + throw new IOException(Messages.getString("Split.0")); } File folder = new File(outputFolder); if (!folder.exists()) { @@ -61,7 +61,7 @@ public static List split(String xliff, String outputFolder) while (it.hasNext()) { String original = it.next().getAttributeValue("original"); if (original.isEmpty()) { - throw new IOException(" without \"original\" attribute"); + throw new IOException(Messages.getString("Split.1")); } originals.add(original); } diff --git a/src/com/maxprograms/swordfish/xliff/XliffStore.java b/src/com/maxprograms/swordfish/xliff/XliffStore.java index 7db735a..e1193c5 100644 --- a/src/com/maxprograms/swordfish/xliff/XliffStore.java +++ b/src/com/maxprograms/swordfish/xliff/XliffStore.java @@ -15,7 +15,6 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.io.StringReader; import java.lang.System.Logger; import java.lang.System.Logger.Level; import java.net.URISyntaxException; @@ -27,6 +26,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.text.MessageFormat; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -48,6 +48,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.jsoup.Jsoup; +import org.sqlite.Function; import org.xml.sax.SAXException; import com.maxprograms.converters.Join; @@ -80,4366 +81,4385 @@ public class XliffStore { - Logger logger = System.getLogger(XliffStore.class.getName()); - - public static final int THRESHOLD = 60; - public static final int MAXTERMLENGTH = 5; - public static final int BATCHSIZE = 100; - - public static final String SVG_BLANK = ""; - public static final String SVG_UNTRANSLATED = ""; - public static final String SVG_TRANSLATED = ""; - public static final String SVG_FINAL = ""; - public static final String SVG_LOCK = ""; - - private String xliffFile; - private SAXBuilder builder; - private Document document; - - private File database; - private Connection conn; - private PreparedStatement insertFile; - private PreparedStatement insertUnit; - private PreparedStatement insertSegmentStmt; - private PreparedStatement insertMatch; - private PreparedStatement updateMatch; - private PreparedStatement getMatches; - private PreparedStatement bestMatch; - private PreparedStatement insertTerm; - private PreparedStatement getTerms; - private PreparedStatement getUnitData; - private PreparedStatement getSource; - private PreparedStatement getTargetStmt; - private PreparedStatement updateTargetStmt; - private PreparedStatement unitMatches; - private PreparedStatement unitTerms; - private PreparedStatement unitNotes; - private PreparedStatement checkTerm; - private PreparedStatement getNotesStmt; - private PreparedStatement insertNoteStmt; - private PreparedStatement getSegment; - - private Statement stmt; - private boolean preserve; - - private static String catalog; - private static boolean acceptUnconfirmed; - private static boolean fuzzyTermSearches; - private static boolean caseSensitiveTermSearches; - private static boolean caseSensitiveMatches; - - private int index; - private int nextId; - private String currentFile; - private String currentUnit; - private String state; - private int tagCount; - - private String srcLang; - private String tgtLang; - - private static int tag; - private Map tagsMap; - private Map notesMap; - - private static Pattern pattern; - private static String lastFilterText; - - public XliffStore(String xliffFile, String sourceLang, String targetLang) - throws SAXException, IOException, ParserConfigurationException, URISyntaxException, SQLException { - - this.xliffFile = xliffFile; - srcLang = sourceLang; - tgtLang = targetLang; - - File xliff = new File(xliffFile); - - database = new File(xliff.getParentFile(), "h2data"); - boolean needsLoading = !database.exists(); - if (!database.exists()) { - database.mkdirs(); - } - getPreferences(); - builder = new SAXBuilder(); - builder.setEntityResolver(new Catalog(catalog)); - - String url = "jdbc:h2:" + database.getAbsolutePath() + "/db"; - conn = DriverManager.getConnection(url); - conn.setAutoCommit(false); - if (needsLoading) { - if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Creating database"); - } - createTables(); - } - try (Statement createNotes = conn.createStatement()) { - createNotes.execute("CREATE TABLE IF NOT EXISTS notes (file VARCHAR(50), unitId VARCHAR(256) NOT NULL, " - + "segId VARCHAR(256) NOT NULL, noteid varchar(256) NOT NULL, note CLOB NOT NULL, PRIMARY KEY(file, unitId, segId, noteid) );"); - } - conn.commit(); - - boolean needsUpgrade = false; - try (Statement stm = conn.createStatement()) { - String sql = "SELECT TYPE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='UNITS' AND COLUMN_NAME='DATA'"; - try (ResultSet rs = stm.executeQuery(sql)) { - while (rs.next()) { - needsUpgrade = !rs.getString(1).equalsIgnoreCase("CLOB"); - } - } - } - if (needsUpgrade) { - String s1 = "ALTER TABLE UNITS ALTER COLUMN DATA SET DATA TYPE CLOB"; - String s2 = "ALTER TABLE SEGMENTS ALTER COLUMN SOURCE SET DATA TYPE CLOB"; - String s3 = "ALTER TABLE SEGMENTS ALTER COLUMN SOURCETEXT SET DATA TYPE CLOB"; - String s4 = "ALTER TABLE SEGMENTS ALTER COLUMN TARGET SET DATA TYPE CLOB"; - String s5 = "ALTER TABLE SEGMENTS ALTER COLUMN TARGETTEXT SET DATA TYPE CLOB"; - String s6 = "ALTER TABLE NOTES ALTER COLUMN NOTE SET DATA TYPE CLOB"; - String s7 = "ALTER TABLE MATCHES ALTER COLUMN SOURCE SET DATA TYPE CLOB"; - String s8 = "ALTER TABLE MATCHES ALTER COLUMN TARGET SET DATA TYPE CLOB"; - String s9 = "ALTER TABLE MATCHES ALTER COLUMN DATA SET DATA TYPE CLOB"; - String s10 = "ALTER TABLE TERMS ALTER COLUMN SOURCE SET DATA TYPE CLOB"; - String s11 = "ALTER TABLE TERMS ALTER COLUMN TARGET SET DATA TYPE CLOB"; - try (Statement upgrade = conn.createStatement()) { - upgrade.execute(s1); - upgrade.execute(s2); - upgrade.execute(s3); - upgrade.execute(s4); - upgrade.execute(s5); - upgrade.execute(s6); - upgrade.execute(s7); - upgrade.execute(s8); - upgrade.execute(s9); - upgrade.execute(s10); - upgrade.execute(s11); - conn.commit(); - } - } - needsUpgrade = true; - try (Statement stm = conn.createStatement()) { - String sql = "SELECT TYPE_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME='SEGMENTS' AND COLUMN_NAME='IDX'"; - try (ResultSet rs = stm.executeQuery(sql)) { - while (rs.next()) { - needsUpgrade = !rs.getString(1).equalsIgnoreCase("INTEGER"); - } - } - } - if (needsUpgrade) { - String s1 = "ALTER TABLE segments ADD COLUMN idx INTEGER"; - try (Statement upgrade = conn.createStatement()) { - upgrade.execute(s1); - } - conn.commit(); - indexSegments(); - } - - getUnitData = conn.prepareStatement("SELECT data, compressed FROM units WHERE file=? AND unitId=?"); - getSource = conn.prepareStatement( - "SELECT source, sourceText, state, translate FROM segments WHERE file=? AND unitId=? AND segId=?"); - getTargetStmt = conn - .prepareStatement("SELECT target, state FROM segments WHERE file=? AND unitId=? AND segId=?"); - updateTargetStmt = conn.prepareStatement( - "UPDATE segments SET target=?, targetText=?, state=? WHERE file=? AND unitId=? AND segId=?"); - insertMatch = conn.prepareStatement( - "INSERT INTO matches (file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed) VALUES(?,?,?,?,?,?,?,?,?,?,?)"); - updateMatch = conn.prepareStatement( - "UPDATE matches SET origin=?, type=?, similarity=?, source=?, target=?, data=?, compressed=? WHERE file=? AND unitId=? AND segId=? AND matchId=?"); - getMatches = conn.prepareStatement( - "SELECT file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed FROM matches WHERE file=? AND unitId=? AND segId=? ORDER BY similarity DESC"); - bestMatch = conn.prepareStatement( - "SELECT type, similarity FROM matches WHERE file=? AND unitId=? AND segId=? ORDER BY similarity DESC LIMIT 1"); - insertTerm = conn.prepareStatement( - "INSERT INTO terms (file, unitId, segId, termid, origin, source, target) VALUES(?,?,?,?,?,?,?)"); - getTerms = conn.prepareStatement( - "SELECT termid, origin, source, target FROM terms WHERE file=? AND unitId=? AND segId=? ORDER BY source"); - checkTerm = conn - .prepareStatement("SELECT target FROM terms WHERE file=? AND unitId=? AND segId=? AND termid=?"); - getNotesStmt = conn.prepareStatement("SELECT noteId, note FROM notes WHERE file=? AND unitId=? AND segId=?"); - getSegment = conn.prepareStatement("SELECT source, target FROM segments WHERE file=? AND unitId=? AND segId=?"); - stmt = conn.createStatement(); - if (needsLoading) { - document = builder.build(xliffFile); - parseDocument(); - conn.commit(); - indexSegments(); - } - } - - private void createTables() throws SQLException { - String files = "CREATE TABLE files (id VARCHAR(50) NOT NULL, name VARCHAR(350) NOT NULL, PRIMARY KEY(id, name));"; - String units = "CREATE TABLE units (file VARCHAR(50), " + "unitId VARCHAR(256) NOT NULL, " - + "data CLOB NOT NULL, compressed CHAR(1) NOT NULL DEFAULT 'N', PRIMARY KEY(file, unitId) );"; - String segments = "CREATE TABLE segments (file VARCHAR(50), unitId VARCHAR(256) NOT NULL, " - + "segId VARCHAR(256) NOT NULL, type CHAR(1) NOT NULL DEFAULT 'S', state VARCHAR(12) DEFAULT 'initial', child INTEGER, " - + "translate CHAR(1), tags INTEGER DEFAULT 0, space CHAR(1) DEFAULT 'N', source CLOB NOT NULL, sourceText CLOB NOT NULL, " - + "target CLOB NOT NULL, targetText CLOB NOT NULL, words INTEGER NOT NULL DEFAULT 0, idx INTEGER, " - + "PRIMARY KEY(file, unitId, segId, type) );"; - String matches = "CREATE TABLE matches (file VARCHAR(50), unitId VARCHAR(256) NOT NULL, " - + "segId VARCHAR(256) NOT NULL, matchId varchar(256), origin VARCHAR(256), type CHAR(2) NOT NULL DEFAULT 'tm', " - + "similarity INTEGER DEFAULT 0, source CLOB NOT NULL, target CLOB NOT NULL, data CLOB NOT NULL, " - + "compressed CHAR(1) NOT NULL DEFAULT 'N', PRIMARY KEY(file, unitId, segId, matchid) );"; - String terms = "CREATE TABLE terms (file VARCHAR(50), unitId VARCHAR(256) NOT NULL, " - + "segId VARCHAR(256) NOT NULL, termid varchar(256), " - + "origin VARCHAR(256), source CLOB NOT NULL, target CLOB NOT NULL, " - + "PRIMARY KEY(file, unitId, segId, termid) );"; - String notes = "CREATE TABLE notes (file VARCHAR(50), unitId VARCHAR(256) NOT NULL, " - + "segId VARCHAR(256) NOT NULL, noteid varchar(256) NOT NULL, note CLOB NOT NULL, PRIMARY KEY(file, unitId, segId, noteid) );"; - try (Statement create = conn.createStatement()) { - create.execute(files); - create.execute(units); - create.execute(segments); - create.execute(matches); - create.execute(terms); - create.execute(notes); - conn.commit(); - } - } - - private void parseDocument() throws SQLException, IOException { - insertFile = conn.prepareStatement("INSERT INTO files (id, name) VALUES (?,?)"); - insertUnit = conn.prepareStatement("INSERT INTO units (file, unitId, data, compressed) VALUES (?,?,?,?)"); - insertSegmentStmt = conn.prepareStatement( - "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); - insertNoteStmt = conn - .prepareStatement("INSERT INTO notes (file, unitId, segId, noteId, note) values (?,?,?,?,?)"); - recurse(document.getRootElement()); - insertFile.close(); - insertUnit.close(); - insertNoteStmt.close(); - insertSegmentStmt.close(); - } - - private void recurse(Element e) throws SQLException, IOException { - if ("file".equals(e.getName())) { - currentFile = e.getAttributeValue("id"); - insertFile.setString(1, currentFile); - insertFile.setNCharacterStream(2, new StringReader(e.getAttributeValue("original"))); - insertFile.execute(); - index = 0; - } - if ("unit".equals(e.getName())) { - tagCount = 0; - nextId = 0; - currentUnit = e.getAttributeValue("id"); - preserve = "preserve".equals(e.getAttributeValue("xml:space", "default")); - JSONObject data = new JSONObject(); - - Element originalData = e.getChild("originalData"); - if (originalData != null) { - List list = originalData.getChildren(); - for (int i = 0; i < list.size(); i++) { - Element d = list.get(i); - data.put(d.getAttributeValue("id"), d.getText()); - tagCount++; - } - } - - Element matches = e.getChild("mtc:matches"); - if (matches != null) { - List m = matches.getChildren("mtc:match"); - Iterator mit = m.iterator(); - while (mit.hasNext()) { - insertMatch(currentFile, currentUnit, mit.next()); - } - } - - notesMap = new Hashtable<>(); - Element notes = e.getChild("notes"); - if (notes != null) { - List n = notes.getChildren("note"); - Iterator nit = n.iterator(); - while (nit.hasNext()) { - Element note = nit.next(); - if (note.hasAttribute("mtc:ref")) { - String segId = note.getAttributeValue("mtc:ref"); - if (segId.startsWith("#")) { - segId = segId.substring(1); - } - insertNote(currentFile, currentUnit, segId, note); - } else { - notesMap.put(note.getAttributeValue("id"), note); - } - } - } - if (tagCount > 0) { - String dataString = data.toString(); - insertUnit.setString(1, currentFile); - insertUnit.setString(2, currentUnit); - insertUnit.setString(3, dataString); - insertUnit.setString(4, "N"); - insertUnit.execute(); - } - Element glossary = e.getChild("gls:glossary"); - if (glossary != null) { - List entries = glossary.getChildren("gls:glossEntry"); - Iterator it = entries.iterator(); - while (it.hasNext()) { - Element glossEntry = it.next(); - if (glossEntry.hasAttribute("ref")) { - String segId = glossEntry.getAttributeValue("ref"); - if (segId.startsWith("#")) { - segId = segId.substring(1); - } - Element term = glossEntry.getChild("gls:term"); - String source = term.getText(); - String origin = term.getAttributeValue("source"); - Element translation = glossEntry.getChild("gls:translation"); - if (translation != null) { - String target = translation.getText(); - saveTerm(currentFile, currentUnit, segId, origin, source, target); - } - } - } - } - } - if ("segment".equals(e.getName())) { - String id = e.getAttributeValue("id"); - if (id.isEmpty()) { - id = "s" + nextId++; - e.setAttribute("id", id); - } - Element source = e.getChild("source"); - boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); - Element target = e.getChild("target"); - if (target == null) { - target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - } - - List segmentNotes = new Vector<>(); - if (!notesMap.isEmpty()) { - segmentNotes.addAll(harvestNotes(source)); - source = FromXliff2.removeComments(source); - segmentNotes.addAll(harvestNotes(target)); - target = FromXliff2.removeComments(target); - } - - state = e.getAttributeValue("state", - XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); - String subState = e.getAttributeValue("subState"); - boolean translate = true; - if ("openxliff:locked".equals(subState)) { - translate = false; - } - preserve = preserve || sourcePreserve - || "preserve".equals(target.getAttributeValue("xml:space", "default")); - - insertSegment(currentFile, currentUnit, id, "S", translate, source, target); - for (int i = 0; i < segmentNotes.size(); i++) { - String noteId = segmentNotes.get(i); - Element note = notesMap.get(noteId); - insertNote(currentFile, currentUnit, id, note); - } - } - if ("ignorable".equals(e.getName())) { - String id = e.getAttributeValue("id"); - if (id.isEmpty()) { - id = "i" + nextId++; - e.setAttribute("id", id); - } - insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); - } - List children = e.getChildren(); - Iterator it = children.iterator(); - while (it.hasNext()) { - recurse(it.next()); - } - if ("file".equals(e.getName())) { - conn.commit(); - } - } - - private List harvestNotes(Element element) { - List result = new Vector<>(); - if ("mrk".equals(element.getName()) && "comment".equals(element.getAttributeValue("type"))) { - if (element.hasAttribute("ref")) { - String ref = element.getAttributeValue("ref"); - result.add(ref.substring(ref.indexOf('=') + 1)); - } - if (element.hasAttribute("value")) { - Element note = new Element("note"); - note.setText(element.getAttributeValue("value")); - String id = "" + (result.size() + 100); - note.setAttribute("id", id); - notesMap.put(id, note); - result.add(id); - } - } - List children = element.getChildren(); - Iterator it = children.iterator(); - while (it.hasNext()) { - result.addAll(harvestNotes(it.next())); - } - return result; - } - - private synchronized void insertSegment(String file, String unit, String segment, String type, boolean translate, - Element source, Element target) throws SQLException { - String pureSource = XliffUtils.pureText(source); - insertSegmentStmt.setString(1, file); - insertSegmentStmt.setString(2, unit); - insertSegmentStmt.setString(3, segment); - insertSegmentStmt.setString(4, type); - insertSegmentStmt.setString(5, state); - insertSegmentStmt.setInt(6, index++); - insertSegmentStmt.setString(7, translate ? "Y" : "N"); - insertSegmentStmt.setInt(8, tagCount); - insertSegmentStmt.setString(9, preserve ? "Y" : "N"); - insertSegmentStmt.setNCharacterStream(10, new StringReader(source.toString())); - insertSegmentStmt.setNCharacterStream(11, new StringReader(pureSource)); - insertSegmentStmt.setNCharacterStream(12, new StringReader(target != null ? target.toString() : "")); - insertSegmentStmt.setNCharacterStream(13, new StringReader(target != null ? XliffUtils.pureText(target) : "")); - insertSegmentStmt.setInt(14, type.equals("S") ? RepetitionAnalysis.wordCount(pureSource, srcLang) : 0); - insertSegmentStmt.execute(); - } - - private void insertMatch(String file, String unit, Element match) throws SQLException, IOException { - Element originalData = match.getChild("originalData"); - Element source = match.getChild("source"); - Element target = match.getChild("target"); - JSONObject tagsData = new JSONObject(); - if (originalData != null) { - List list = originalData.getChildren(); - for (int i = 0; i < list.size(); i++) { - Element d = list.get(i); - tagsData.put(d.getAttributeValue("id"), d.getText()); - } - } - String segment = match.getAttributeValue("ref"); - if (segment.startsWith("#")) { - segment = segment.substring(1); - } - String type = match.getAttributeValue("type", Constants.TM); - String origin = match.getAttributeValue("origin"); - int similarity = Math.round(Float.parseFloat(match.getAttributeValue("matchQuality", "0.0"))); - - insertMatch(file, unit, segment, origin, type, similarity, source, target, tagsData); - } - - private void insertNote(String file, String unit, String segId, Element note) throws SQLException { - insertNoteStmt.setString(1, file); - insertNoteStmt.setString(2, unit); - insertNoteStmt.setString(3, segId); - insertNoteStmt.setString(4, note.getAttributeValue("id", unit)); - insertNoteStmt.setNCharacterStream(5, new StringReader(note.getText())); - insertNoteStmt.execute(); - } - - public int size() throws SQLException { - int count = 0; - String sql = "SELECT count(*) FROM segments WHERE type='S'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - count = rs.getInt(1); - } - } - return count; - } - - public void saveXliff() throws IOException { - XMLOutputter outputter = new XMLOutputter(); - outputter.preserveSpace(true); - Indenter.indent(document.getRootElement(), 2); - try (FileOutputStream out = new FileOutputStream(xliffFile)) { - outputter.output(document, out); - } - } - - public synchronized List getSegments(int start, int count, String filterText, String filterLanguage, - boolean caseSensitiveFilter, boolean regExp, boolean showUntranslated, boolean showTranslated, - boolean showConfirmed, String sortOption, boolean sortDesc) - throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { - List result = new Vector<>(); - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append( - "SELECT file, unitId, segId, child, source, target, tags, state, space, translate, sourceText, targetText, idx FROM segments WHERE type='S'"); - if (!filterText.isEmpty()) { - if (regExp) { - try { - Pattern.compile(filterText); - } catch (PatternSyntaxException e) { - throw new IOException("Invalid regular expression"); - } - queryBuilder.append(" AND REGEXP_LIKE("); - if ("source".equals(filterLanguage)) { - queryBuilder.append("sourceText,'"); - } else { - queryBuilder.append("targetText,'"); - } - queryBuilder.append(filterText); - if (caseSensitiveFilter) { - queryBuilder.append("','c')"); - } else { - queryBuilder.append("','i')"); - } - } else { - if (caseSensitiveFilter) { - if ("source".equals(filterLanguage)) { - queryBuilder.append(" AND sourceText LIKE '%"); - } else { - queryBuilder.append(" AND targetText LIKE '%"); - } - } else { - if ("source".equals(filterLanguage)) { - queryBuilder.append(" AND sourceText ILIKE '%"); - } else { - queryBuilder.append(" AND targetText ILIKE '%"); - } - } - queryBuilder.append(escape(filterText)); - queryBuilder.append("%'"); - } - if (!showUntranslated) { - queryBuilder.append(" AND state <> 'initial'"); - } - if (!showTranslated) { - queryBuilder.append(" AND state <> 'translated'"); - } - if (!showConfirmed) { - queryBuilder.append(" AND state <> 'final'"); - } - } - if (sortOption.equals("none")) { - queryBuilder.append(" ORDER BY file, child "); - } - if (sortOption.equals("source")) { - queryBuilder.append(" ORDER BY sourceText"); - } - if (sortOption.equals("target")) { - queryBuilder.append(" ORDER BY targetText"); - } - if (sortOption.equals("status")) { - queryBuilder.append(" ORDER BY state"); - } - if (sortDesc) { - queryBuilder.append(" DESC "); - } - queryBuilder.append(" LIMIT "); - queryBuilder.append(count); - queryBuilder.append(" OFFSET "); - queryBuilder.append(start); - try (ResultSet rs = stmt.executeQuery(queryBuilder.toString())) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segId = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(5)); - String tgt = TMUtils.getString(rs.getNCharacterStream(6)); - int tags = rs.getInt(7); - String segState = rs.getString(8); - boolean segPreserve = "Y".equals(rs.getString(9)); - boolean segTranslate = "Y".equals(rs.getString(10)); - String sourceText = TMUtils.getString(rs.getNCharacterStream(11)); - String targetText = TMUtils.getString(rs.getNCharacterStream(12)); - int idx = rs.getInt(13); - - JSONObject tagsData = new JSONObject(); - if (tags > 0) { - tagsData = getUnitData(file, unit); - } - Element source = XliffUtils.buildElement(src); - - Element target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - if (tgt != null && !tgt.isBlank()) { - target = XliffUtils.buildElement(tgt); - } - - boolean checkErrors = segTranslate - && (segState.equals("final") || (segState.equals("translated") && acceptUnconfirmed)); - - boolean tagErrors = false; - boolean spaceErrors = false; - if (checkErrors) { - tagErrors = hasTagErrors(source, target); - spaceErrors = hasSpaceErrors(sourceText, targetText); - } - tagsMap = new Hashtable<>(); - JSONObject row = new JSONObject(); - row.put("index", idx); - row.put("file", file); - row.put("unit", unit); - row.put("segment", segId); - row.put("state", segState); - row.put("translate", segTranslate); - row.put("preserve", segPreserve); - tag = 1; - row.put("source", addHtmlTags(source, filterText, caseSensitiveFilter, regExp, tagsData, segPreserve)); - tag = 1; - row.put("target", addHtmlTags(target, filterText, caseSensitiveFilter, regExp, tagsData, segPreserve)); - row.put("match", getBestMatch(file, unit, segId)); - row.put("hasNotes", hasNotes(file, unit, segId)); - row.put("tagErrors", tagErrors); - row.put("spaceErrors", spaceErrors); - result.add(row); - } - } - return result; - } - - private boolean hasNotes(String file, String unit, String segId) throws SQLException { - boolean result = false; - getNotesStmt.setString(1, file); - getNotesStmt.setString(2, unit); - getNotesStmt.setString(3, segId); - try (ResultSet rs = getNotesStmt.executeQuery()) { - while (rs.next()) { - result = true; - } - } - return result; - } - - public JSONArray getNotes(String file, String unit, String segId) throws SQLException, IOException { - JSONArray array = new JSONArray(); - getNotesStmt.setString(1, file); - getNotesStmt.setString(2, unit); - getNotesStmt.setString(3, segId); - try (ResultSet rs = getNotesStmt.executeQuery()) { - while (rs.next()) { - JSONObject note = new JSONObject(); - note.put("id", rs.getString(1)); - note.put("note", TMUtils.getString(rs.getNCharacterStream(2))); - array.put(note); - } - } - return array; - } - - public JSONArray addNote(String file, String unit, String segId, String noteText) throws SQLException, IOException { - String sql = "SELECT noteId FROM notes WHERE file=? AND unitId=? AND segId=?"; - int maxId = 0; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segId); - try (ResultSet rs = prep.executeQuery()) { - while (rs.next()) { - String id = rs.getString(1); - try { - int number = Integer.parseInt(id); - if (number > maxId) { - maxId = number; - } - } catch (NumberFormatException e) { - // ignore - } - } - } - } - sql = "INSERT INTO notes (file, unitId, segId, noteId, note) values (?,?,?,?,?)"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segId); - prep.setString(4, "" + (maxId + 1)); - prep.setNCharacterStream(5, new StringReader(noteText)); - prep.executeUpdate(); - } - conn.commit(); - JSONArray array = new JSONArray(); - getNotesStmt.setString(1, file); - getNotesStmt.setString(2, unit); - getNotesStmt.setString(3, segId); - try (ResultSet rs = getNotesStmt.executeQuery()) { - while (rs.next()) { - JSONObject note = new JSONObject(); - note.put("id", rs.getString(1)); - note.put("note", TMUtils.getString(rs.getNCharacterStream(2))); - array.put(note); - } - } - return array; - } - - public JSONArray removeNote(String file, String unit, String segId, String noteId) - throws SQLException, IOException { - String sql = "DELETE FROM notes WHERE file=? AND unitId=? AND segId=? AND noteId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segId); - prep.setString(4, noteId); - prep.executeUpdate(); - } - conn.commit(); - JSONArray array = new JSONArray(); - getNotesStmt.setString(1, file); - getNotesStmt.setString(2, unit); - getNotesStmt.setString(3, segId); - try (ResultSet rs = getNotesStmt.executeQuery()) { - while (rs.next()) { - JSONObject note = new JSONObject(); - note.put("id", rs.getString(1)); - note.put("note", TMUtils.getString(rs.getNCharacterStream(2))); - array.put(note); - } - } - return array; - } - - private boolean hasTagErrors(Element source, Element target) { - List sourceTags = tagsList(source); - List targetTags = tagsList(target); - if (sourceTags.size() != targetTags.size()) { - return true; - } - for (int i = 0; i < sourceTags.size(); i++) { - if (!sourceTags.get(i).equals(targetTags.get(i))) { - return true; - } - } - return false; - } - - private boolean hasSpaceErrors(String sourceText, String targetText) { - int[] sourceSpaces = countSpaces(sourceText); - int[] targetSpaces = countSpaces(targetText); - return sourceSpaces[0] != targetSpaces[0] || sourceSpaces[1] != targetSpaces[1]; - } - - private synchronized int getBestMatch(String file, String unit, String segment) throws SQLException { - String type = ""; - int similarity = 0; - bestMatch.setString(1, file); - bestMatch.setString(2, unit); - bestMatch.setString(3, segment); - try (ResultSet rs = bestMatch.executeQuery()) { - while (rs.next()) { - type = rs.getString(1); - similarity = rs.getInt(2); - } - } - if (type.isEmpty() || Constants.MT.equals(type) || Constants.AM.equals(type)) { - return 0; - } - return similarity; - } - - private synchronized JSONObject getUnitData(String file, String unit) throws SQLException, DataFormatException { - getUnitData.setString(1, file); - getUnitData.setString(2, unit); - String data = ""; - boolean compressed = false; - try (ResultSet rs = getUnitData.executeQuery()) { - while (rs.next()) { - data = rs.getString(1); - compressed = "Y".equals(rs.getString(2)); - } - } - if (data.isEmpty()) { - return new JSONObject(); - } - if (compressed) { - return new JSONObject(Compression.decompress(data)); - } - return new JSONObject(data); - } - - public void close() throws SQLException { - getUnitData.close(); - getSource.close(); - getTargetStmt.close(); - updateTargetStmt.close(); - insertMatch.close(); - updateMatch.close(); - getMatches.close(); - bestMatch.close(); - insertTerm.close(); - getTerms.close(); - checkTerm.close(); - getNotesStmt.close(); - getSegment.close(); - stmt.close(); - conn.commit(); - conn.close(); - if (TmsServer.isDebug()) { - logger.log(Level.INFO, "Closed store"); - } - } - - public String getSrcLang() { - return srcLang; - } - - public String getTgtLang() { - return tgtLang; - } - - private static void getPreferences() throws IOException { - JSONObject json = TmsServer.getPreferences(); - acceptUnconfirmed = json.getBoolean("acceptUnconfirmed"); - caseSensitiveTermSearches = json.getBoolean("caseSensitiveSearches"); - caseSensitiveMatches = true; - if (json.has("caseSensitiveMatches")) { - caseSensitiveMatches = json.getBoolean("caseSensitiveMatches"); - } - fuzzyTermSearches = json.getBoolean("fuzzyTermSearches"); - catalog = json.getString("catalog"); - } - - public synchronized JSONObject saveSegment(JSONObject json) - throws IOException, SQLException, SAXException, ParserConfigurationException, DataFormatException { - - JSONObject result = new JSONObject(); - - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - String translation = json.getString("translation").replace(" ", "\u00A0").replace("
", "\n"); - boolean confirm = json.getBoolean("confirm"); - String memory = json.getString("memory"); - - String src = ""; - String pureSource = ""; - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - boolean wasFinal = false; - boolean translatable = true; - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - src = TMUtils.getString(rs.getNCharacterStream(1)); - pureSource = TMUtils.getString(rs.getNCharacterStream(2)); - wasFinal = rs.getString(3).equals("final"); - translatable = rs.getString(4).equals("Y"); - } - } - Element source = XliffUtils.buildElement(src); - - Map tags = getTags(source); - - translation = XliffUtils.clearHTML(translation); - - List list = XliffUtils.harvestTags(translation); - if (!list.isEmpty()) { - for (int i = 0; i < list.size(); i++) { - String code = list.get(i)[0]; - String img = list.get(i)[1]; - if (tags.containsKey(code)) { - translation = replace(translation, img, tags.get(code)); - } else { - translation = replace(translation, img, ""); - } - } - } - Element translated = XliffUtils.buildElement("" + translation + ""); - Element target = getTarget(file, unit, segment); - boolean unchanged = target.getContent().equals(translated.getContent()); - - target.setContent(translated.getContent()); - String pureTarget = XliffUtils.pureText(target); - JSONArray propagated = new JSONArray(); - updateTarget(file, unit, segment, target, pureTarget, confirm); - if (confirm && !pureTarget.isBlank() && (!unchanged || !wasFinal)) { - propagated = propagate(source, target); - } - result.put("propagated", propagated); - - boolean checkErrors = translatable && (confirm || !pureTarget.isEmpty()); - - boolean tagErrors = false; - boolean spaceErrors = false; - if (checkErrors) { - tagErrors = hasTagErrors(source, target); - spaceErrors = hasSpaceErrors(pureSource, pureTarget); - } - - result.put("tagErrors", tagErrors); - result.put("spaceErrors", spaceErrors); - - if (!memory.equals(Constants.NONE) && !pureTarget.isBlank() && confirm) { - new Thread(() -> { - try { - StringBuilder key = new StringBuilder(); - key.append(xliffFile.hashCode()); - key.append('-'); - key.append(file); - key.append('-'); - key.append(unit); - key.append('-'); - key.append(segment); - MemoriesHandler.open(memory); - ITmEngine engine = MemoriesHandler.getEngine(memory); - engine.storeTu(XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang)); - engine.commit(); - MemoriesHandler.close(memory); - } catch (IOException | SQLException | URISyntaxException e) { - logger.log(Level.ERROR, e); - } - }).start(); - } - return result; - } - - public synchronized void saveSource(JSONObject json) - throws IOException, SQLException, SAXException, ParserConfigurationException { - - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - String newSource = json.getString("newSource").replace(" ", "\u00A0").replace("
", "\n"); - - String src = ""; - String pureSource = ""; - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - src = TMUtils.getString(rs.getNCharacterStream(1)); - pureSource = TMUtils.getString(rs.getNCharacterStream(2)); - } - } - Element source = XliffUtils.buildElement(src); - - Map tags = getTags(source); - - newSource = XliffUtils.clearHTML(newSource); - - List list = XliffUtils.harvestTags(newSource); - if (!list.isEmpty()) { - for (int i = 0; i < list.size(); i++) { - String code = list.get(i)[0]; - String img = list.get(i)[1]; - if (tags.containsKey(code)) { - newSource = replace(newSource, img, tags.get(code)); - } else { - newSource = replace(newSource, img, ""); - } - } - } - Element updated = XliffUtils.buildElement("" + newSource + ""); - if (source.getContent().equals(updated.getContent())) { - return; - } - - source.setContent(updated.getContent()); - pureSource = XliffUtils.pureText(source); - - String sql = "UPDATE segments SET source=?, sourceText=? WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setNCharacterStream(1, new StringReader(source.toString())); - prep.setNCharacterStream(2, new StringReader(pureSource)); - prep.setString(3, file); - prep.setString(4, unit); - prep.setString(5, segment); - prep.executeUpdate(); - } - } - - public synchronized JSONObject getTranslationStatus() throws SQLException { - JSONObject result = new JSONObject(); - int total = 0; - int translated = 0; - int confirmed = 0; - int segments = 0; - String sql = "SELECT SUM(words), COUNT(*) FROM segments WHERE type='S'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - total = rs.getInt(1); - segments = rs.getInt(2); - } - } - sql = "SELECT SUM(words) FROM segments WHERE state='final' AND type='S'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - confirmed = rs.getInt(1); - } - } - sql = "SELECT SUM(words) FROM segments WHERE state <> 'initial' AND type='S'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - translated = rs.getInt(1); - } - } - int percentage = 0; - if (total != 0) { - percentage = Math.round(confirmed * 100f / total); - } - - result.put("segments", segments); - result.put("total", total); - result.put("translated", translated); - result.put("confirmed", confirmed); - result.put("percentage", percentage); - result.put("text", "Segments: " + segments + "\u00A0\u00A0\u00A0Words: " + total - + "\u00A0\u00A0\u00A0Translated: " + translated + "\u00A0\u00A0\u00A0Confirmed: " + confirmed); - result.put("svg", XliffUtils.makeSVG(percentage)); - return result; - } - - private synchronized void updateTarget(String file, String unit, String segment, Element target, String pureTarget, - boolean confirm) throws SQLException { - String segState = pureTarget.isBlank() ? Constants.INITIAL : Constants.TRANSLATED; - if (confirm) { - segState = Constants.FINAL; - } - updateTargetStmt.setNCharacterStream(1, new StringReader(target.toString())); - updateTargetStmt.setNCharacterStream(2, new StringReader(pureTarget)); - updateTargetStmt.setString(3, segState); - updateTargetStmt.setString(4, file); - updateTargetStmt.setString(5, unit); - updateTargetStmt.setString(6, segment); - updateTargetStmt.executeUpdate(); - conn.commit(); - } - - private JSONArray propagate(Element source, Element target) - throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { - JSONArray result = new JSONArray(); - String dummySource = dummyTagger(source); - String query = "SELECT file, unitId, segId, source, state, tags, space FROM segments WHERE translate='Y' AND type='S' AND state <> 'final' "; - try (ResultSet rs = stmt.executeQuery(query)) { - while (rs.next()) { - Element candidate = XliffUtils.buildElement(TMUtils.getString(rs.getNCharacterStream(4))); - int differences = tagDifferences(source, candidate); - String dummy = dummyTagger(candidate); - int similarity = MatchQuality.similarity(dummySource, dummy) - differences; - if (similarity > THRESHOLD) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - Element sourceElement = XliffUtils.buildElement(rs.getNString(4)); - int tags = rs.getInt(6); - JSONObject tagsData = new JSONObject(); - if (tags > 0) { - tagsData = getUnitData(file, unit); - } - if (similarity == 100 && Constants.INITIAL.equals(state)) { - tagsMap = new Hashtable<>(); - tag = 1; - addHtmlTags(candidate, "", false, false, tagsData, true); - - JSONObject row = new JSONObject(); - row.put("file", file); - row.put("unit", unit); - row.put("segment", segment); - row.put("match", 100); - tag = 1; - String translation = addHtmlTags(target, "", false, false, tagsData, true); - row.put("target", translation); - result.put(row); - - Element translated = XliffUtils.buildElement("" + translation + ""); - translated.setAttribute("xml:space", preserve ? "preserve" : "default"); - translated.setContent(target.getContent()); - if (!translated.getChildren().isEmpty()) { - translated = fixTags(sourceElement, source, target); - } - updateTarget(file, unit, segment, translated, XliffUtils.pureText(translated), false); - } - insertMatch(file, unit, segment, "Self", Constants.TM, similarity, source, target, tagsData); - conn.commit(); - int best = getBestMatch(file, unit, segment); - JSONObject row = new JSONObject(); - row.put("file", file); - row.put("unit", unit); - row.put("segment", segment); - row.put("match", best); - result.put(row); - } - } - } - return result; - } - - private int tagDifferences(Element source, Element candidate) { - int a = source.getChildren().size(); - int b = candidate.getChildren().size(); - if (a > b) { - return a - b; - } - return b - a; - } - - private synchronized void insertMatch(String file, String unit, String segment, String origin, String type, - int similarity, Element source, Element target, JSONObject tagsData) throws SQLException, IOException { - String matchId = "" + XliffUtils.pureText(source).hashCode() * origin.hashCode(); - if (Constants.MT.equals(type)) { - matchId = origin; - } - JSONArray matches = getMatches(file, unit, segment); - String data = ""; - if (!source.getChildren().isEmpty() || !target.getChildren().isEmpty()) { - List added = new Vector<>(); - Element originalData = new Element("originalData"); - List children = source.getChildren(); - Iterator it = children.iterator(); - while (it.hasNext()) { - Element e = it.next(); - if ("mrk".equals(e.getName()) || "pc".equals(e.getName()) || "cp".equals(e.getName())) { - continue; - } - String dataRef = e.getAttributeValue("dataRef"); - if (!added.contains(dataRef) && tagsData.has(dataRef)) { - Element d = new Element("data"); - d.setAttribute("id", dataRef); - d.setText(tagsData.getString(dataRef)); - originalData.addContent(d); - added.add(dataRef); - continue; - } - e.removeAttribute("dataRef"); - } - children = target.getChildren(); - it = children.iterator(); - while (it.hasNext()) { - Element e = it.next(); - if ("mrk".equals(e.getName()) || "pc".equals(e.getName()) || "cp".equals(e.getName())) { - continue; - } - String dataRef = e.getAttributeValue("dataRef"); - if (added.contains(dataRef)) { - continue; - } - if (!added.contains(dataRef) && tagsData.has(dataRef)) { - Element d = new Element("data"); - d.setAttribute("id", dataRef); - d.setText(tagsData.getString(dataRef)); - originalData.addContent(d); - added.add(dataRef); - continue; - } - e.removeAttribute("dataRef"); - } - data = originalData.toString(); - } - for (int i = 0; i < matches.length(); i++) { - JSONObject match = matches.getJSONObject(i); - if (match.getString("matchId").equals(matchId)) { - updateMatch.setString(1, origin); - updateMatch.setString(2, type); - updateMatch.setInt(3, similarity); - updateMatch.setNCharacterStream(4, new StringReader(source.toString())); - updateMatch.setNCharacterStream(5, new StringReader(target.toString())); - updateMatch.setString(6, data); - updateMatch.setString(7, "N"); - updateMatch.setString(8, file); - updateMatch.setString(9, unit); - updateMatch.setString(10, segment); - updateMatch.setString(11, matchId); - updateMatch.execute(); - return; - } - } - insertMatch.setString(1, file); - insertMatch.setString(2, unit); - insertMatch.setString(3, segment); - insertMatch.setString(4, matchId); - insertMatch.setString(5, origin); - insertMatch.setString(6, type); - insertMatch.setInt(7, similarity); - insertMatch.setNCharacterStream(8, new StringReader(source.toString())); - insertMatch.setNCharacterStream(9, new StringReader(target.toString())); - insertMatch.setString(10, data); - insertMatch.setString(11, "N"); - insertMatch.execute(); - } - - private JSONArray getMatches(String file, String unit, String segment) throws SQLException, IOException { - JSONArray result = new JSONArray(); - getMatches.setString(1, file); - getMatches.setString(2, unit); - getMatches.setString(3, segment); - try (ResultSet rs = getMatches.executeQuery()) { - while (rs.next()) { - JSONObject match = new JSONObject(); - match.put("file", file); - match.put("unit", unit); - match.put("segment", segment); - match.put("matchId", rs.getString(4)); - match.put("origin", rs.getString(5)); - match.put("type", rs.getString(6)); - match.put("similarity", rs.getInt(7)); - match.put("source", TMUtils.getString(rs.getNCharacterStream(8))); - match.put("srcLang", srcLang); - match.put("target", TMUtils.getString(rs.getNCharacterStream(9))); - match.put("tgtLang", tgtLang); - result.put(match); - } - } - return result; - } - - private String dummyTagger(Element e) { - if (e == null) { - return ""; - } - int dummy = 1; - StringBuilder string = new StringBuilder(); - List content = e.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode n = it.next(); - if (n.getNodeType() == XMLNode.TEXT_NODE) { - TextNode t = (TextNode) n; - string.append(t.getText()); - } - if (n.getNodeType() == XMLNode.ELEMENT_NODE) { - Element el = (Element) n; - if ("mrk".equals(el.getName()) || "pc".equals(el.getName())) { - string.append((char) (0xF300 + dummy++)); - string.append(dummyTagger(el)); - string.append((char) (0xF300 + dummy++)); - } else { - string.append((char) (0xF300 + dummy++)); - } - } - } - return string.toString(); - } - - private String addHtmlTags(Element seg, JSONObject originalData) throws IOException { - if (seg == null) { - return ""; - } - List list = seg.getContent(); - Iterator it = list.iterator(); - StringBuilder text = new StringBuilder(); - while (it.hasNext()) { - XMLNode o = it.next(); - if (o.getNodeType() == XMLNode.TEXT_NODE) { - text.append(XliffUtils.cleanString(((TextNode) o).getText())); - } else if (o.getNodeType() == XMLNode.ELEMENT_NODE) { - // empty: , , , , and . - // paired: , , - Element e = (Element) o; - String type = e.getName(); - if (type.equals("pc")) { - String id = e.getAttributeValue("id"); - if (!tagsMap.containsKey("pc" + id)) { - XliffUtils.checkSVG(tag); - String header = XliffUtils.getHeader(e); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("pc" + id, sb.toString()); - } - text.append(tagsMap.get("pc" + id)); - text.append(addHtmlTags(e, originalData)); - if (!tagsMap.containsKey("/pc" + id)) { - XliffUtils.checkSVG(tag); - String tail = ""; - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("/pc" + id, sb.toString()); - } - text.append("/" + tagsMap.get(e.getName() + id)); - } else if (type.equals("mrk")) { - String id = e.getAttributeValue("id"); - if (!tagsMap.containsKey("mrk" + id)) { - XliffUtils.checkSVG(tag); - String header = XliffUtils.getHeader(e); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("mrk" + id, sb.toString()); - } - text.append(tagsMap.get(e.getName() + id)); - text.append(""); - text.append(e.getText()); - text.append(""); - if (!tagsMap.containsKey("/mrk" + id)) { - XliffUtils.checkSVG(tag); - String tail = ""; - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("/mrk" + id, sb.toString()); - } - text.append(tagsMap.get("/mrk" + id)); - } else if (type.equals("cp")) { - String hex = "cp" + e.getAttributeValue("hex"); - if (!tagsMap.containsKey(hex)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put(hex, sb.toString()); - } - text.append(tagsMap.get(hex)); - } else { - String dataRef = e.getAttributeValue("dataRef"); - if (!tagsMap.containsKey(dataRef)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put(dataRef, sb.toString()); - } - text.append(tagsMap.get(dataRef)); - } - } - } - return text.toString(); - } - - private String addHtmlTags(Element seg, String filterText, boolean caseSensitive, boolean regExp, - JSONObject originalData, boolean preserve) throws IOException { - if (seg == null) { - return ""; - } - List list = seg.getContent(); - Iterator it = list.iterator(); - StringBuilder text = new StringBuilder(); - while (it.hasNext()) { - XMLNode o = it.next(); - if (o.getNodeType() == XMLNode.TEXT_NODE) { - if (filterText == null || filterText.isEmpty()) { - text.append(XliffUtils.cleanString(((TextNode) o).getText())); - } else { - if (regExp) { - if (pattern == null || !filterText.equals(lastFilterText)) { - pattern = Pattern.compile(filterText); - lastFilterText = filterText; - } - String s = ((TextNode) o).getText(); - Matcher matcher = pattern.matcher(s); - if (matcher.find()) { - StringBuilder sb = new StringBuilder(); - do { - int start = matcher.start(); - int end = matcher.end(); - sb.append(XliffUtils.cleanString(s.substring(0, start))); - sb.append(""); - sb.append(XliffUtils.cleanString(s.substring(start, end))); - sb.append(""); - s = s.substring(end); - matcher = pattern.matcher(s); - } while (matcher.find()); - sb.append(XliffUtils.cleanString(s)); - text.append(sb.toString()); - } else { - text.append(XliffUtils.cleanString(s)); - } - } else { - String s = XliffUtils.cleanString(((TextNode) o).getText()); - String t = XliffUtils.cleanString(filterText); - if (caseSensitive) { - if (s.indexOf(t) != -1) { - text.append(XliffUtils.highlight(s, t, caseSensitive)); - } else { - text.append(s); - } - } else { - if (s.toLowerCase().indexOf(t.toLowerCase()) != -1) { - text.append(XliffUtils.highlight(s, t, caseSensitive)); - } else { - text.append(s); - } - } - } - } - } else if (o.getNodeType() == XMLNode.ELEMENT_NODE) { - text.append(inline2html((Element) o, originalData)); - } - } - return preserve ? XliffUtils.highlightSpaces(text.toString()) : text.toString().trim(); - } - - private String inline2html(Element e, JSONObject originalData) throws IOException { - // empty: , , , , and . - // paired: , , - StringBuilder text = new StringBuilder(); - String type = e.getName(); - if (type.equals("pc")) { - String id = e.getAttributeValue("id"); - if (!tagsMap.containsKey("pc" + id)) { - XliffUtils.checkSVG(tag); - String header = XliffUtils.getHeader(e); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("pc" + id, sb.toString()); - } - text.append(tagsMap.get("pc" + id)); - List content = e.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.TEXT_NODE) { - String s = ((TextNode) node).getText(); - text.append(XMLUtils.cleanText(s)); - } - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - text.append(inline2html((Element) node, originalData)); - } - } - if (!tagsMap.containsKey("/pc" + id)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""))); - sb.append("\"/>"); - tagsMap.put("/pc" + id, sb.toString()); - } - text.append(tagsMap.get("/pc" + id)); - } else if (type.equals("mrk")) { - String id = e.getAttributeValue("id"); - boolean isTerm = e.getAttributeValue("type").equals("term"); - if (!isTerm) { - if (!tagsMap.containsKey("mrk" + id)) { - XliffUtils.checkSVG(tag); - String header = XliffUtils.getHeader(e); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("mrk" + id, sb.toString()); - } - text.append(tagsMap.get(e.getName() + id)); - text.append(""); - } else { - text.append(""); - } - List content = e.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.TEXT_NODE) { - String s = ((TextNode) node).getText(); - text.append(XMLUtils.cleanText(s)); - } - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - text.append(inline2html((Element) node, originalData)); - } - } - text.append(""); - if (!isTerm) { - if (!tagsMap.containsKey("/mrk" + id)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""))); - sb.append("\"/>"); - tagsMap.put("/mrk" + id, sb.toString()); - } - text.append(tagsMap.get("/mrk" + id)); - } - } else if (type.equals("cp")) { - String hex = "cp" + e.getAttributeValue("hex"); - if (!tagsMap.containsKey(hex)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put(hex, sb.toString()); - } - text.append(tagsMap.get(hex)); - } else if ("ph".equals(type)) { - String id = e.getAttributeValue("id"); - if (!tagsMap.containsKey("ph" + id)) { - XliffUtils.checkSVG(tag); - String title = originalData.has(id) ? originalData.getString(id) : e.toString(); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put("ph" + id, sb.toString()); - } - text.append(tagsMap.get("ph" + id)); - } else { - String dataRef = e.getAttributeValue("dataRef"); - if (!tagsMap.containsKey(dataRef)) { - XliffUtils.checkSVG(tag); - StringBuilder sb = new StringBuilder(); - sb.append(""); - tagsMap.put(dataRef, sb.toString()); - } - text.append(tagsMap.get(dataRef)); - } - return text.toString(); - } - - private String replace(String source, String target, String replacement) { - int start = source.indexOf(target); - while (start != -1) { - source = source.substring(0, start) + replacement + source.substring(start + target.length()); - start += replacement.length(); - start = source.indexOf(target, start); - } - return source; - } - - private Map getTags(Element root) { - Map result = new Hashtable<>(); - List content = root.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("mrk".equals(e.getName()) || "pc".equals(e.getName())) { - result.put(e.getAttributeValue("id"), XliffUtils.getHeader(e)); - result.put("/" + e.getAttributeValue("id"), XliffUtils.getTail(e)); - Map map = getTags(e); - result.putAll(map); - } else if ("cp".equals(e.getName())) { - result.put("cp" + e.getAttributeValue("hex"), e.toString()); - } else { - result.put(e.getAttributeValue("id"), e.toString()); - } - } - } - return result; - } - - public synchronized JSONArray getTaggedtMatches(JSONObject json) - throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { - JSONArray result = new JSONArray(); - - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - - JSONObject originalData = getUnitData(file, unit); - Element originalSource = null; - - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - String src = TMUtils.getString(rs.getNCharacterStream(1)); - originalSource = XliffUtils.buildElement(src); - } - } - List originalTags = originalSource.getChildren(); - String dummySource = dummyTagger(originalSource); - - getMatches.setString(1, file); - getMatches.setString(2, unit); - getMatches.setString(3, segment); - try (ResultSet rs = getMatches.executeQuery()) { - while (rs.next()) { - tag = 1; - JSONObject match = new JSONObject(); - match.put("file", file); - match.put("unit", unit); - match.put("segment", segment); - match.put("matchId", rs.getString(4)); - match.put("origin", rs.getString(5)); - match.put("type", rs.getString(6)); - match.put("similarity", rs.getInt(7)); - match.put("srcLang", srcLang); - match.put("tgtLang", tgtLang); - - String src = TMUtils.getString(rs.getNCharacterStream(8)); - Element source = XliffUtils.buildElement(src); - String tgt = TMUtils.getString(rs.getNCharacterStream(9)); - Element target = XliffUtils.buildElement(tgt); - - List sourceTags = source.getChildren(); - List targetTags = target.getChildren(); - - for (int i = 0; i < sourceTags.size(); i++) { - Element sourceTag = sourceTags.get(i); - for (int j = 0; j < targetTags.size(); j++) { - Element targetTag = targetTags.get(j); - if (sourceTag.equals(targetTag) && i < originalTags.size()) { - targetTag.clone(originalTags.get(i)); - } - } - if (i < originalTags.size()) { - sourceTag.clone(originalTags.get(i)); - } - } - - String taggedSource = addHtmlTags(source, originalData); - - List tags = XliffUtils.harvestTags(taggedSource); - for (int i = 0; i < tags.size(); i++) { - taggedSource = taggedSource.replace(tags.get(i)[1], "" + (char) (0xF300 + (i + 1))); - } - - DifferenceTagger tagger = new DifferenceTagger(dummySource, taggedSource); - String tagged = tagger.getYDifferences(); - for (int i = 0; i < tags.size(); i++) { - tagged = tagged.replace("" + (char) (0xF300 + (i + 1)), tags.get(i)[1]); - } - - match.put("source", tagged); - match.put("target", addHtmlTags(target, "", false, false, originalData, true)); - result.put(match); - } - } - return result; - } - - public void exportXliff(String output) - throws SAXException, IOException, ParserConfigurationException, SQLException { - updateXliff(); - File projectFolder = new File(xliffFile).getParentFile(); - File tempFolder = new File(projectFolder, "tmp"); - if (tempFolder.exists()) { - TmsServer.deleteFolder(tempFolder); - } - List files = Split.split(xliffFile, tempFolder.getAbsolutePath()); - File tempFile = File.createTempFile("joined", ".xlf", tempFolder); - Join.join(files, tempFile.getAbsolutePath()); - File outputFile = new File(output); - if (outputFile.exists()) { - Files.delete(outputFile.toPath()); - } - Files.copy(tempFile.toPath(), outputFile.toPath()); - Files.delete(tempFile.toPath()); - TmsServer.deleteFolder(tempFolder); - } - - public void exportTMX(String output, String description, String client, String subject) - throws SQLException, SAXException, IOException, ParserConfigurationException { - Element d = new Element("prop"); - d.setAttribute("type", "project"); - d.setText(description); - Element c = null; - if (!client.isBlank()) { - c = new Element("prop"); - c.setAttribute("type", "customer"); - c.setText(client); - } - Element s = null; - if (!subject.isBlank()) { - s = new Element("prop"); - s.setAttribute("type", "subject"); - s.setText(subject); - } - try (FileOutputStream out = new FileOutputStream(output)) { - writeTmxHeader(out); - String sql = "SELECT file, unitId, segId, source, target FROM segments WHERE type='S' AND state='final' ORDER BY SELECT file, unitId, segId"; - - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - - StringBuilder key = new StringBuilder(); - key.append(xliffFile.hashCode()); - key.append('-'); - key.append(file); - key.append('-'); - key.append(unit); - key.append('-'); - key.append(segment); - - String src = TMUtils.getString(rs.getNCharacterStream(4)); - Element source = XliffUtils.buildElement(src); - String tgt = TMUtils.getString(rs.getNCharacterStream(5)); - Element target = XliffUtils.buildElement(tgt); - - Map tags = getTags(source); - - Element tuv = XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang); - tuv.getContent().add(0, d); - if (c != null) { - tuv.getContent().add(0, c); - } - if (s != null) { - tuv.getContent().add(0, s); - } - Indenter.indent(tuv, 2); - writeString(out, tuv.toString()); - } - } - writeString(out, "\n"); - writeString(out, "\n"); - } - } - - private void writeTmxHeader(FileOutputStream out) throws IOException { - writeString(out, "\n"); - writeString(out, - "\n"); - writeString(out, "\n"); - writeString(out, - "
\n"); - writeString(out, "\n"); - } - - public void exportTranslations(String output) - throws SAXException, IOException, ParserConfigurationException, SQLException { - updateXliff(); - getPreferences(); - File adjusted = reviewStates(); - List result = Merge.merge(adjusted.getAbsolutePath(), output, catalog, acceptUnconfirmed); - if (!"0".equals(result.get(0))) { - throw new IOException(result.get(1)); - } - Files.delete(adjusted.toPath()); - } - - private File reviewStates() throws SAXException, IOException, ParserConfigurationException { - File xliff = new File(xliffFile); - File adjusted = new File(xliff.getParentFile(), "adjusted.xlf"); - document = builder.build(xliffFile); - recurseStates(document.getRootElement()); - XMLOutputter outputter = new XMLOutputter(); - outputter.preserveSpace(true); - Indenter.indent(document.getRootElement(), 2); - try (FileOutputStream out = new FileOutputStream(adjusted)) { - outputter.output(document, out); - } - return adjusted; - } - - private void recurseStates(Element e) { - if ("segment".equals(e.getName())) { - if ("initial".equals(e.getAttributeValue("state"))) { - Element source = e.getChild("source"); - Element target = e.getChild("target"); - if (target == null) { - target = new Element("target"); - if ("preserve".equals(source.getAttributeValue("xml:space", "default"))) { - target.setAttribute("xml:space", "preserve"); - } - e.addContent(target); - } - target.setContent(source.getContent()); - } - return; - } - if ("ignorable".equals(e.getName())) { - Element target = e.getChild("target"); - if (target == null) { - Element source = e.getChild("source"); - target = new Element("target"); - if ("preserve".equals(source.getAttributeValue("xml:space", "default"))) { - target.setAttribute("xml:space", "preserve"); - } - target.setContent(source.getContent()); - e.addContent(target); - } - return; - } - List children = e.getChildren(); - Iterator it = children.iterator(); - while (it.hasNext()) { - recurseStates(it.next()); - } - } - - public void updateXliff() throws SQLException, SAXException, IOException, ParserConfigurationException { - document = builder.build(xliffFile); - document.getRootElement().setAttribute("xmlns:mtc", "urn:oasis:names:tc:xliff:matches:2.0"); - document.getRootElement().setAttribute("xmlns:gls", "urn:oasis:names:tc:xliff:glossary:2.0"); - unitMatches = conn.prepareStatement( - "SELECT file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed FROM matches WHERE file=? AND unitId=? ORDER BY segId, similarity DESC"); - unitTerms = conn.prepareStatement( - "SELECT file, unitId, segId, termId, origin, source, target FROM terms WHERE file=? AND unitId=? ORDER BY segId"); - unitNotes = conn - .prepareStatement("SELECT segId, noteId, note FROM notes WHERE file=? AND unitId=? ORDER BY segId"); - recurseUpdating(document.getRootElement()); - unitTerms.close(); - unitMatches.close(); - unitNotes.close(); - saveXliff(); - } - - private void recurseUpdating(Element e) - throws SQLException, SAXException, IOException, ParserConfigurationException { - if ("file".equals(e.getName())) { - currentFile = e.getAttributeValue("id"); - index = 0; - } - if ("unit".equals(e.getName())) { - tagCount = 0; - currentUnit = e.getAttributeValue("id"); - Element glossary = getUnitTerms(currentFile, currentUnit); - if (glossary != null) { - insertGlossary(e, glossary); - } - Element matches = getUnitMatches(currentFile, currentUnit); - Map matchesData = new HashMap<>(); - if (matches != null) { - List matchesList = matches.getChildren("mtc:match"); - Iterator it = matchesList.iterator(); - while (it.hasNext()) { - Element match = it.next(); - Element originalData = match.getChild("originalData"); - if (originalData != null) { - List dataList = originalData.getChildren("data"); - for (int i = 0; i < dataList.size(); i++) { - Element data = dataList.get(i); - matchesData.put(data.getAttributeValue("id"), data); - } - } - } - insertMatches(e, matches); - } - if (!matchesData.isEmpty()) { - Element originalData = e.getChild("originalData"); - if (originalData == null) { - originalData = new Element("originalData"); - insertOriginalData(e, originalData); - } - Map oldData = new HashMap<>(); - List dataList = originalData.getChildren("data"); - for (int i = 0; i < dataList.size(); i++) { - Element data = dataList.get(i); - oldData.put(data.getAttributeValue("id"), data); - } - Set keys = matchesData.keySet(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - String key = it.next(); - if (!oldData.containsKey(key)) { - oldData.put(key, matchesData.get(key)); - originalData.addContent(matchesData.get(key)); - } - } - } - Element notes = getUnitNotes(currentFile, currentUnit); - if (notes != null) { - insertNotes(e, notes); - } - } - if ("segment".equals(e.getName())) { - String id = e.getAttributeValue("id"); - Element source = e.getChild("source"); - Element target = e.getChild("target"); - if (target == null) { - target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - e.addContent(target); - } - Element updated = getTarget(currentFile, currentUnit, id); - target.setContent(updated.getContent()); - String st = getState(currentFile, currentUnit, id); - boolean translate = isTranslatable(currentFile, currentUnit, id); - if (Constants.INITIAL.equals(st) && !target.getContent().isEmpty()) { - st = Constants.TRANSLATED; - logger.log(Level.WARNING, "Changing segment state from 'initial' to 'translated'"); - } - JSONArray notesArray = getNotes(currentFile, currentUnit, id); - if (notesArray.length() > 0) { - target = FromXliff2.removeComments(target); - for (int i = 0; i < notesArray.length(); i++) { - JSONObject json = notesArray.getJSONObject(i); - String noteId = json.getString("id"); - Element mrk = new Element("mrk"); - mrk.setAttribute("id", "tn" + noteId); - mrk.setAttribute("type", "comment"); - mrk.setAttribute("ref", "#n=" + noteId); - mrk.setContent(target.getContent()); - List content = new Vector<>(); - content.add(mrk); - target.setContent(content); - } - } - e.setAttribute("state", st); - if (translate) { - e.removeAttribute("subState"); - } else { - e.setAttribute("subState", "openxliff:locked"); - } - if (Constants.INITIAL.equals(st) && target.getContent().isEmpty()) { - e.removeChild(target); - } - } - if ("ignorable".equals(e.getName())) { - Element source = e.getChild("source"); - Element target = e.getChild("target"); - if (target == null) { - target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - e.addContent(target); - } - target.setContent(source.getContent()); - } - List children = e.getChildren(); - Iterator it = children.iterator(); - while (it.hasNext()) { - recurseUpdating(it.next()); - } - } - - private void insertOriginalData(Element unit, Element originalData) { - List newContent = new Vector<>(); - boolean added = false; - List oldContent = unit.getContent(); - Iterator it = oldContent.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("segment".equals(e.getName()) || "ignorable".equals(e.getName())) { - if (!added) { - newContent.add(originalData); - added = true; - } - } - newContent.add(node); - } else { - newContent.add(node); - } - } - unit.setContent(newContent); - } - - private void insertMatches(Element unit, Element matches) { - Element old = unit.getChild("mtc:matches"); - if (old != null) { - unit.removeChild(old); - } - unit.getContent().add(0, matches); - } - - private void insertNotes(Element unit, Element notes) { - Element old = unit.getChild("notes"); - if (old != null) { - unit.removeChild(old); - } - boolean added = false; - List newContent = new Vector<>(); - List oldContent = unit.getContent(); - Iterator it = oldContent.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if (e.getNamespace().isEmpty() && !added) { - newContent.add(notes); - added = true; - } - newContent.add(node); - } else { - newContent.add(node); - } - } - unit.setContent(newContent); - } - - private void insertGlossary(Element unit, Element terms) { - Element old = unit.getChild("gls:glossary"); - if (old != null) { - unit.removeChild(old); - } - unit.getContent().add(0, terms); - } - - private Element getUnitMatches(String file, String unit) - throws SQLException, SAXException, IOException, ParserConfigurationException { - Element matches = new Element("mtc:matches"); - unitMatches.setString(1, file); - unitMatches.setString(2, unit); - try (ResultSet rs = unitMatches.executeQuery()) { - while (rs.next()) { - Element match = new Element("mtc:match"); - match.setAttribute("ref", "#" + rs.getString(3)); - match.setAttribute("origin", rs.getString(5)); - match.setAttribute("type", rs.getString(6)); - match.setAttribute("matchQuality", "" + rs.getInt(7)); - Element originalData = new Element("originalData"); - String data = TMUtils.getString(rs.getNCharacterStream(10)); - if (!data.isEmpty()) { - originalData = XliffUtils.buildElement(data); - List newData = new Vector<>(); - List oldData = originalData.getChildren(); - Iterator it = oldData.iterator(); - while (it.hasNext()) { - Element d = it.next(); - if (!d.getContent().isEmpty()) { - newData.add(d); - } - } - originalData.setChildren(newData); - if (!newData.isEmpty()) { - match.addContent(originalData); - } - } - Set dataRefs = new HashSet<>(); - Iterator it = originalData.getChildren().iterator(); - while (it.hasNext()) { - dataRefs.add(it.next().getAttributeValue("id")); - } - match.addContent(XliffUtils.buildElement(TMUtils.getString(rs.getNCharacterStream(8)))); - match.addContent(XliffUtils.buildElement(TMUtils.getString(rs.getNCharacterStream(9)))); - removeMissingReferences(match.getChild("source"), dataRefs); - removeMissingReferences(match.getChild("target"), dataRefs); - matches.addContent(match); - } - } - return matches.getChildren("mtc:match").isEmpty() ? null : matches; - } - - private void removeMissingReferences(Element child, Set references) { - List tags = child.getChildren(); - Iterator it = tags.iterator(); - while (it.hasNext()) { - Element e = it.next(); - String dataRef = e.getAttributeValue("dataRef"); - if (!dataRef.isEmpty() && !references.contains(dataRef)) { - e.removeAttribute("dataRef"); - } - } - } - - private Element getUnitNotes(String file, String unit) throws SQLException, IOException { - Element notes = new Element("notes"); - unitNotes.setString(1, file); - unitNotes.setString(2, unit); - try (ResultSet rs = unitNotes.executeQuery()) { - while (rs.next()) { - Element note = new Element("note"); - note.setAttribute("id", rs.getString(2)); - note.setText(TMUtils.getString(rs.getNCharacterStream(3))); - notes.addContent(note); - } - } - return notes.getChildren().isEmpty() ? null : notes; - } - - private Element getUnitTerms(String file, String unit) throws SQLException, IOException { - Element glossary = new Element("gls:glossary"); - unitTerms.setString(1, file); - unitTerms.setString(2, unit); - try (ResultSet rs = unitTerms.executeQuery()) { - while (rs.next()) { - Element entry = new Element("gls:glossEntry"); - entry.setAttribute("ref", "#" + rs.getString(3)); - glossary.addContent(entry); - - Element term = new Element("gls:term"); - term.setAttribute("source", rs.getString(5)); - term.setText(TMUtils.getString(rs.getNCharacterStream(6))); - entry.addContent(term); - - Element translation = new Element("gls:translation"); - translation.setText(TMUtils.getString(rs.getNCharacterStream(7))); - entry.addContent(translation); - } - } - return glossary.getChildren().isEmpty() ? null : glossary; - } - - public void assembleMatches(JSONObject json) - throws SAXException, IOException, ParserConfigurationException, SQLException, URISyntaxException { - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - - String pure = ""; - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - pure = TMUtils.getString(rs.getNCharacterStream(2)); - } - } - - String memory = json.getString("memory"); - MemoriesHandler.open(memory); - ITmEngine tmEngine = MemoriesHandler.getEngine(memory); - List tmMatches = tmEngine.searchTranslation(pure, srcLang, tgtLang, 60, false); - MemoriesHandler.close(memory); - - String glossary = json.getString("glossary"); - GlossariesHandler.openGlossary(glossary); - ITmEngine glossEngine = GlossariesHandler.getEngine(glossary); - - Match match = MatchAssembler.assembleMatch(pure, tmMatches, glossEngine, srcLang, tgtLang); - if (match != null) { - Element matchSource = match.getSource(); - matchSource.setAttribute("xml:lang", srcLang); - Element matchTarget = match.getTarget(); - matchTarget.setAttribute("xml:lang", tgtLang); - insertMatch(file, unit, segment, "Auto", Constants.AM, match.getSimilarity(), matchSource, matchTarget, - new JSONObject()); - conn.commit(); - } - GlossariesHandler.closeGlossary(glossary); - } - - public void assembleMatchesAll(JSONObject json) - throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { - - String memory = json.getString("memory"); - MemoriesHandler.open(memory); - ITmEngine tmEngine = MemoriesHandler.getEngine(memory); - - String glossary = json.getString("glossary"); - GlossariesHandler.openGlossary(glossary); - ITmEngine glossEngine = GlossariesHandler.getEngine(glossary); - - String sql = "SELECT file, unitId, segId, sourceText FROM segments WHERE state <> 'final'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String pure = TMUtils.getString(rs.getNCharacterStream(4)); - try { - List tmMatches = tmEngine.searchTranslation(pure, srcLang, tgtLang, 60, false); - Match match = MatchAssembler.assembleMatch(pure, tmMatches, glossEngine, srcLang, tgtLang); - if (match != null) { - Element matchSource = match.getSource(); - matchSource.setAttribute("xml:lang", srcLang); - Element matchTarget = match.getTarget(); - matchTarget.setAttribute("xml:lang", tgtLang); - insertMatch(file, unit, segment, "Auto", Constants.AM, match.getSimilarity(), matchSource, - matchTarget, new JSONObject()); - conn.commit(); - } - } catch (IOException | ParserConfigurationException | SAXException | SQLException ex) { - // Ignore errors in individual segments - JSONObject errorSegment = new JSONObject(); - errorSegment.put("file", file); - errorSegment.put("unit", unit); - errorSegment.put("segment", segment); - logger.log(Level.WARNING, - "Error assembling matches: " + ex.getMessage() + "\n" + errorSegment.toString()); - } - } - } - MemoriesHandler.close(memory); - GlossariesHandler.closeGlossary(glossary); - } - - public JSONArray tmTranslate(JSONObject json) - throws SAXException, IOException, ParserConfigurationException, SQLException, DataFormatException, - URISyntaxException { - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - String memory = json.getString("memory"); - - String src = ""; - String pure = ""; - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - src = TMUtils.getString(rs.getNCharacterStream(1)); - pure = TMUtils.getString(rs.getNCharacterStream(2)); - } - } - Element original = XliffUtils.buildElement(src); - String memoryName = MemoriesHandler.getName(memory); - MemoriesHandler.open(memory); - ITmEngine engine = MemoriesHandler.getEngine(memory); - List matches = engine.searchTranslation(pure, srcLang, tgtLang, 60, caseSensitiveMatches); - for (int i = 0; i < matches.size(); i++) { - Match m = matches.get(i); - XliffUtils.setTags(new JSONObject()); - Element matchSource = XliffUtils.toXliff(segment, i, "source", m.getSource()); - matchSource.setAttribute("xml:lang", srcLang); - Element matchTarget = XliffUtils.toXliff(segment, i, "target", m.getTarget()); - matchTarget.setAttribute("xml:lang", tgtLang); - JSONObject obj = new JSONObject(); - obj.put("dataRef", XliffUtils.getTags()); - int similarity = m.getSimilarity() - tagDifferences(original, matchSource); - insertMatch(file, unit, segment, memoryName, Constants.TM, similarity, matchSource, matchTarget, obj); - conn.commit(); - } - MemoriesHandler.close(memory); - return getTaggedtMatches(json); - } - - public int tmTranslateAll(String memory, int penalization, Map processes, String processId) - throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { - String memoryName = MemoriesHandler.getName(memory); - MemoriesHandler.open(memory); - ITmEngine engine = MemoriesHandler.getEngine(memory); - String sql = "SELECT COUNT(*) FROM segments WHERE type = 'S' AND state <> 'final'"; - int total = 0; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - total = rs.getInt(1); - } - } - if (total == 0) { - return 0; - } - int processed = 0; - int offset = 0; - do { - processed += translateBatch(offset, engine, memoryName, penalization); - int percentage = Math.round(offset * 100f / total); - if (percentage == 100) { - percentage = 99; - } - processes.get(processId).put("percentage", percentage); - offset += BATCHSIZE; - } while (offset < total); - MemoriesHandler.close(memory); - return processed; - } - - private synchronized int translateBatch(int offset, ITmEngine engine, String memoryName, int penalization) - throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { - String sql = "SELECT file, unitId, segId, source, sourceText, target FROM segments WHERE type = 'S' AND state <> 'final' OFFSET ?"; - JSONObject params = new JSONObject(); - JSONArray array = new JSONArray(); - try (PreparedStatement prepared = conn.prepareStatement(sql)) { - prepared.setInt(1, offset); - try (ResultSet rs = prepared.executeQuery()) { - params.put("srcLang", srcLang); - params.put("tgtLang", tgtLang); - params.put("caseSensitiveMatches", caseSensitiveMatches); - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String pure = TMUtils.getString(rs.getNCharacterStream(5)); - JSONObject json = new JSONObject(); - json.put("file", file); - json.put("unit", unit); - json.put("segment", segment); - json.put("pure", pure); - array.put(json); - if (array.length() == BATCHSIZE) { - break; - } - } - params.put("segments", array); - } - } - JSONArray translations = engine.batchTranslate(params); - return storeMatches(translations, memoryName, penalization); - } - - private int storeMatches(JSONArray translations, String memoryName, int penalization) - throws SAXException, IOException, ParserConfigurationException, SQLException { - int count = 0; - for (int i = 0; i < translations.length(); i++) { - JSONObject json = translations.getJSONObject(i); - JSONArray matches = json.getJSONArray("matches"); - if (matches.length() > 0) { - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - - getSegment.setString(1, file); - getSegment.setString(2, unit); - getSegment.setString(3, segment); - try (ResultSet rs = getSegment.executeQuery()) { - while (rs.next()) { - String src = TMUtils.getString(rs.getNCharacterStream(1)); - String tgt = TMUtils.getString(rs.getNCharacterStream(2)); - Element original = XliffUtils.buildElement(src); - if (tgt == null || tgt.isEmpty()) { - tgt = ""; - } - Element originalTarget = XliffUtils.buildElement(tgt); - boolean updated = false; - for (int j = 0; j < matches.length(); j++) { - Match m = new Match(matches.getJSONObject(j)); - XliffUtils.setTags(new JSONObject()); - Element matchSource = XliffUtils.toXliff(segment, j, "source", m.getSource()); - matchSource.setAttribute("xml:lang", srcLang); - Element matchTarget = XliffUtils.toXliff(segment, j, "target", m.getTarget()); - matchTarget.setAttribute("xml:lang", tgtLang); - int similarity = m.getSimilarity() - tagDifferences(original, matchSource) - penalization; - insertMatch(file, unit, segment, memoryName, Constants.TM, similarity, matchSource, - matchTarget, XliffUtils.getTags()); - if (similarity == 100 && originalTarget.getContent().isEmpty() && !updated) { - if (!matchTarget.getChildren().isEmpty()) { - matchTarget = fixTags(original, matchSource, matchTarget); - } - updateTarget(file, unit, segment, matchTarget, XliffUtils.pureText(matchTarget), false); - updated = true; - } - } - } - } - conn.commit(); - count++; - } - } - return count; - } - - private Element fixTags(Element source, Element matchSource, Element matchTarget) { - List sourceTags = source.getChildren(); - List matchSourceTags = matchSource.getChildren(); - List matchTargetTags = matchTarget.getChildren(); - for (int i = 0; i < matchTargetTags.size(); i++) { - Element targetTag = matchTargetTags.get(i); - for (int j = 0; j < matchSourceTags.size(); j++) { - Element sourceTag = matchSourceTags.get(j); - if (sourceTag.equals(targetTag)) { - if (j < sourceTags.size()) { - Element originalTag = sourceTags.get(j); - targetTag.clone(originalTag); - } - break; - } - } - } - return matchTarget; - } - - public void removeTranslations() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND translate='Y' and targetText<>'' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(4)); - Element source = XliffUtils.buildElement(src); - Element target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - updateTarget(file, unit, segment, target, "", false); - } - } - } - - public void unconfirmTranslations() throws SQLException { - stmt.execute("UPDATE segments SET state='initial' WHERE type='S' AND targetText='' AND translate='Y' "); - stmt.execute("UPDATE segments SET state='translated' WHERE type='S' AND targetText <> '' AND translate='Y' "); - conn.commit(); - } - - public void pseudoTranslate() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(4)); - - Element source = XliffUtils.buildElement(src); - Element target = pseudoTranslate(source); - String pureTarget = XliffUtils.pureText(target); - - updateTarget(file, unit, segment, target, pureTarget, false); - } - } - } - - private Element pseudoTranslate(Element source) { - Element target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - List content = source.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.TEXT_NODE) { - TextNode t = (TextNode) node; - target.addContent(pseudoTranslate(t.getText())); - } - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("g".equals(e.getName())) { - e.setText(pseudoTranslate(e.getText())); - } - target.addContent(e); - } - } - return target; - } - - private String pseudoTranslate(String text) { - String result = text.replace('a', '\u00E3'); - result = result.replace('e', '\u00E8'); - result = result.replace('i', '\u00EE'); - result = result.replace('o', '\u00F4'); - result = result.replace('A', '\u00C4'); - result = result.replace('E', '\u00CB'); - result = result.replace('I', '\u00CF'); - result = result.replace('O', '\u00D5'); - result = result.replace('U', '\u00D9'); - return result; - } - - public void copyAllSources() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(4)); - - Element source = XliffUtils.buildElement(src); - Element target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - target.setContent(source.getContent()); - String pureTarget = XliffUtils.pureText(target); - - updateTarget(file, unit, segment, target, pureTarget, false); - } - } - } - - public void confirmAllTranslations(String memory) - throws SQLException, SAXException, IOException, ParserConfigurationException, URISyntaxException { - if (memory.equals(Constants.NONE)) { - stmt.execute("UPDATE segments SET state='final' WHERE type='S' AND targetText<>'' AND translate='Y' "); - conn.commit(); - return; - } - MemoriesHandler.open(memory); - ITmEngine engine = MemoriesHandler.getEngine(memory); - String sql = "SELECT file, unitId, segId, FROM segments WHERE state<>'final' AND type='S' AND targetText<>'' AND translate='Y'"; - try (ResultSet rs = stmt.executeQuery(sql)) { - try (PreparedStatement updateSegment = conn - .prepareStatement("UPDATE segments SET state='final' WHERE file=? AND unitId=? AND segId=?")) { - - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - - updateSegment.setString(1, file); - updateSegment.setString(2, unit); - updateSegment.setString(3, segment); - updateSegment.executeUpdate(); - - getSegment.setString(1, file); - getSegment.setString(2, unit); - getSegment.setString(3, segment); - - try (ResultSet rs2 = getSegment.executeQuery()) { - while (rs2.next()) { - Element source = XliffUtils.buildElement(TMUtils.getString(rs2.getNCharacterStream(1))); - Element target = XliffUtils.buildElement(TMUtils.getString(rs2.getNCharacterStream(2))); - Map tags = getTags(source); - StringBuilder key = new StringBuilder(); - key.append(xliffFile.hashCode()); - key.append('-'); - key.append(file); - key.append('-'); - key.append(unit); - key.append('-'); - key.append(segment); - engine.storeTu(XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang)); - } - } - } - } - } - MemoriesHandler.close(memory); - conn.commit(); - } - - public void acceptAll100Matches() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - try (PreparedStatement perfectMatches = conn.prepareStatement( - "SELECT source, target FROM matches WHERE file=? AND unitId=? AND segId=? AND type='tm' AND similarity=100 LIMIT 1")) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(4)); - Element originalSource = XliffUtils.buildElement(src); - - perfectMatches.setString(1, file); - perfectMatches.setString(2, unit); - perfectMatches.setString(3, segment); - try (ResultSet rs2 = perfectMatches.executeQuery()) { - while (rs2.next()) { - Element source = XliffUtils.buildElement(TMUtils.getString(rs2.getNCharacterStream(1))); - String tgt = TMUtils.getString(rs2.getNCharacterStream(2)); - Element target = XliffUtils.buildElement(tgt); - target.setAttribute("xml:lang", tgtLang); - if (originalSource.hasAttribute("xml:space")) { - target.setAttribute("xml:space", originalSource.getAttributeValue("xml:space")); - } - String pureTarget = XliffUtils.pureText(target); - if (!target.getChildren().isEmpty()) { - target = fixTags(originalSource, source, target); - } - updateTarget(file, unit, segment, target, pureTarget, false); - } - } - } - } - } - } - - public String generateStatistics() - throws SQLException, SAXException, IOException, ParserConfigurationException, URISyntaxException { - getPreferences(); - updateXliff(); - File file = new File(xliffFile); - - Map map = new HashMap<>(); - Map> filesMap = new HashMap<>(); - - String sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type='S' GROUP BY file"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - JSONObject json = new JSONObject(); - int words = rs.getInt(2); - int segments = rs.getInt(3); - json.put("file", fileId); - json.put("words", words); - json.put("segments", segments); - map.put(fileId, json); - } - } - - sql = "SELECT id, name FROM files"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String id = rs.getString(1); - String name = rs.getNString(2); - map.get(id).put("name", name); - if (filesMap.containsKey(name)) { - Set ids = filesMap.get(name); - ids.add(id); - filesMap.put(name, ids); - } else { - Set ids = new TreeSet<>(); - ids.add(id); - filesMap.put(name, ids); - } - } - } - - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext = '' GROUP BY file"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - int untranslated = rs.getInt(2); - int untranslatedSegments = rs.getInt(3); - map.get(fileId).put("untranslated", untranslated); - map.get(fileId).put("untranslatedSegments", untranslatedSegments); - } - } - - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext <> '' GROUP BY file"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - int translated = rs.getInt(2); - int translatedSegments = rs.getInt(3); - map.get(fileId).put("translated", translated); - map.get(fileId).put("translatedSegments", translatedSegments); - } - } - - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND state = 'final' GROUP BY file"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - int confirmed = rs.getInt(2); - int confirmedSegments = rs.getInt(3); - map.get(fileId).put("confirmed", confirmed); - map.get(fileId).put("confirmedSegments", confirmedSegments); - } - } - - Map statusMap = new HashMap<>(); - Set currentFileSegments = null; - Set otherFileSegments = new TreeSet<>(); - - sql = "SELECT MAX(similarity) FROM matches WHERE file = ? AND unitid = ? AND segid = ?"; - try (PreparedStatement st = conn.prepareStatement(sql)) { - String currentFileId = ""; - JSONObject json = null; - sql = "SELECT file, unitid, segid, source, words,tags FROM segments WHERE type = 'S' ORDER BY file, unitid, segid"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - String unitId = rs.getString(2); - String segId = rs.getString(3); - String source = rs.getNString(4); - int words = rs.getInt(5); - int tags = rs.getInt(6); - if (!currentFileId.equals(fileId)) { - json = new JSONObject(); - json.put("newSegments", 0); - json.put("100Segments", 0); - json.put("95Segments", 0); - json.put("85Segments", 0); - json.put("75Segments", 0); - json.put("50Segments", 0); - json.put("intRepSegment", 0); - json.put("extRepSegment", 0); - json.put("newWords", 0); - json.put("100Words", 0); - json.put("95Words", 0); - json.put("85Words", 0); - json.put("75Words", 0); - json.put("50Words", 0); - json.put("tags", 0); - json.put("intRep", 0); - json.put("extRep", 0); - statusMap.put(fileId, json); - currentFileId = fileId; - if (currentFileSegments != null) { - otherFileSegments.addAll(currentFileSegments); - } - currentFileSegments = new TreeSet<>(); - } - json.put("tags", json.getInt("tags") + tags); - st.setString(1, fileId); - st.setString(2, unitId); - st.setString(3, segId); - int max = 0; - try (ResultSet rs2 = st.executeQuery()) { - while (rs2.next()) { - max = rs2.getInt(1); - } - } - if (max < 50) { - if (currentFileSegments.contains(source)) { - json.put("intRepSegment", json.getInt("intRepSegment") + 1); - json.put("intRep", json.getInt("intRep") + words); - } else if (otherFileSegments.contains(source)) { - json.put("extRepSegment", json.getInt("extRepSegment") + 1); - json.put("extRep", json.getInt("extRep") + words); - } else { - json.put("newSegments", json.getInt("newSegments") + 1); - json.put("newWords", json.getInt("newWords") + words); - } - } - currentFileSegments.add(source); - if (max == 100) { - json.put("100Segments", json.getInt("100Segments") + 1); - json.put("100Words", json.getInt("100Words") + words); - } - if (max >= 95 && max <= 99) { - json.put("95Segments", json.getInt("95Segments") + 1); - json.put("95Words", json.getInt("95Words") + words); - } - if (max >= 85 && max <= 94) { - json.put("85Segments", json.getInt("85Segments") + 1); - json.put("85Words", json.getInt("85Words") + words); - } - if (max >= 75 && max <= 84) { - json.put("75Segments", json.getInt("75Segments") + 1); - json.put("75Words", json.getInt("75Words") + words); - } - if (max >= 50 && max <= 74) { - json.put("50Segments", json.getInt("50Segments") + 1); - json.put("50Words", json.getInt("50Words") + words); - } - } - } - } - - sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND translate = 'N' GROUP BY file"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String fileId = rs.getString(1); - int confirmed = rs.getInt(2); - int confirmedSegments = rs.getInt(3); - map.get(fileId).put("locked", confirmed); - map.get(fileId).put("lockedSegments", confirmedSegments); - } - } - - Set keys = map.keySet(); - Iterator it = keys.iterator(); - while (it.hasNext()) { - String key = it.next(); - JSONObject json = map.get(key); - if (!json.has("untranslated")) { - json.put("untranslated", 0); - map.put(key, json); - } - if (!json.has("translated")) { - json.put("translated", 0); - map.put(key, json); - } - if (!json.has("confirmed")) { - json.put("confirmed", 0); - map.put(key, json); - } - if (!json.has("translatedSegments")) { - json.put("translatedSegments", 0); - map.put(key, json); - } - if (!json.has("untranslatedSegments")) { - json.put("untranslatedSegments", 0); - map.put(key, json); - } - if (!json.has("confirmedSegments")) { - json.put("confirmedSegments", 0); - map.put(key, json); - } - if (!json.has("locked")) { - json.put("locked", 0); - map.put(key, json); - } - if (!json.has("lockedSegments")) { - json.put("lockedSegments", 0); - map.put(key, json); - } - } - - File log = new File(file.getAbsolutePath() + ".log.html"); - try (FileOutputStream out = new FileOutputStream(log)) { - - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, " \n"); - writeString(out, " Project Statistics\n"); - writeString(out, " \n"); - writeString(out, "\n"); - writeString(out, "\n"); - - writeString(out, "

" + XMLUtils.cleanText(file.getName()) + "

\n"); - - Set files = new TreeSet<>(); - files.addAll(filesMap.keySet()); - it = files.iterator(); - int count = 1; - - int projectNew = 0; - int project100 = 0; - int project95 = 0; - int project85 = 0; - int project75 = 0; - int project50 = 0; - int projectIntRep = 0; - int projectExtRep = 0; - - writeString(out, "
\n"); - writeString(out, "

TM & Repetition Analysis

\n"); - writeString(out, "

Segments

\n"); - - writeString(out, "
Swordfish Eclipse Public License 1.0H2 Eclipse Public License 1.0
SQLitePublic Domain
JSON JSON.org
\n"); - writeString(out, - "\n"); - while (it.hasNext()) { - String fileName = it.next(); - Set set = filesMap.get(fileName); - Iterator st = set.iterator(); - - int newSegments = 0; - int intRep = 0; - int extRep = 0; - int segments100 = 0; - int segments95 = 0; - int segments85 = 0; - int segments75 = 0; - int segments50 = 0; - - while (st.hasNext()) { - String key = st.next(); - JSONObject json = statusMap.get(key); - newSegments += json.getInt("newSegments"); - intRep += json.getInt("intRepSegment"); - extRep += json.getInt("extRepSegment"); - segments100 += json.getInt("100Segments"); - segments95 += json.getInt("95Segments"); - segments85 += json.getInt("85Segments"); - segments75 += json.getInt("75Segments"); - segments50 += json.getInt("50Segments"); - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - projectNew += newSegments; - project100 += segments100; - project95 += segments95; - project85 += segments85; - project75 += segments75; - project50 += segments50; - projectIntRep += intRep; - projectExtRep += extRep; - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - writeString(out, "
#DocumentNew100%95% - 99%85% - 95%75% - 84%50% - 84%Int. Rep.Ext. Rep.Total
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + newSegments + "" + segments100 + "" + segments95 + "" + segments85 + "" + segments75 + "" + segments50 + "" + intRep + "" + extRep + "" - + (newSegments + segments100 + segments95 + segments85 + segments75 + segments50 + intRep - + extRep) - + "
 Total" + projectNew + "" + project100 + "" + project95 + "" + project85 + "" + project75 + "" + project50 + "" + projectIntRep + "" + projectExtRep + "" - + (projectNew + project100 + project95 + project85 + project75 + project50 + projectIntRep - + projectExtRep) - + "
\n"); - - it = files.iterator(); - count = 1; - - projectNew = 0; - project100 = 0; - project95 = 0; - project85 = 0; - project75 = 0; - project50 = 0; - projectIntRep = 0; - projectExtRep = 0; - int projectTags = 0; - writeString(out, "

Words

\n"); - - writeString(out, "\n"); - writeString(out, - "\n"); - while (it.hasNext()) { - String fileName = it.next(); - Set set = filesMap.get(fileName); - Iterator st = set.iterator(); - - int newSegments = 0; - int intRep = 0; - int extRep = 0; - int segments100 = 0; - int segments95 = 0; - int segments85 = 0; - int segments75 = 0; - int segments50 = 0; - int tags = 0; - - while (st.hasNext()) { - String key = st.next(); - JSONObject json = statusMap.get(key); - newSegments += json.getInt("newWords"); - intRep += json.getInt("intRep"); - extRep += json.getInt("extRep"); - segments100 += json.getInt("100Words"); - segments95 += json.getInt("95Words"); - segments85 += json.getInt("85Words"); - segments75 += json.getInt("75Words"); - segments50 += json.getInt("50Words"); - tags += json.getInt("tags"); - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - projectNew += newSegments; - project100 += segments100; - project95 += segments95; - project85 += segments85; - project75 += segments75; - project50 += segments50; - projectIntRep += intRep; - projectExtRep += extRep; - projectTags += tags; - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - writeString(out, "
#DocumentNew100%95% - 99%85% - 95%75% - 84%50% - 84%Int. Rep.Ext. Rep.TagsTotal
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + newSegments + "" + segments100 + "" + segments95 + "" + segments85 + "" + segments75 + "" + segments50 + "" + intRep + "" + extRep + "" + tags + "" - + (newSegments + segments100 + segments95 + segments85 + segments75 + segments50 + intRep - + extRep) - + "
 Total" + projectNew + "" + project100 + "" + project95 + "" + project85 + "" + project75 + "" + project50 + "" + projectIntRep + "" + projectExtRep + "" + projectTags + "" - + (projectNew + project100 + project95 + project85 + project75 + project50 + projectIntRep - + projectExtRep) - + "
\n"); - - writeString(out, "

Int. Rep.: Internal Repetition - Segment repetitions within one document
"); - writeString(out, "Ext. Rep.: External Repetition - Segment repetitions between all documents

"); - - it = files.iterator(); - count = 1; - int projectSegments = 0; - int projectTranslatedSegments = 0; - int projectUntranslatedSegments = 0; - int projectConfirmedSegments = 0; - - writeString(out, "
\n"); - writeString(out, "

Translation Status

\n"); - writeString(out, "

Segments

\n"); - - writeString(out, "\n"); - writeString(out, - "\n"); - while (it.hasNext()) { - String fileName = it.next(); - Set set = filesMap.get(fileName); - Iterator st = set.iterator(); - int fileSegments = 0; - int fileTranslated = 0; - int fileUntranslated = 0; - int fileConfirmed = 0; - while (st.hasNext()) { - String key = st.next(); - JSONObject json = map.get(key); - fileSegments += json.getInt("segments"); - fileTranslated += json.getInt("translatedSegments"); - fileUntranslated += json.getInt("untranslatedSegments"); - fileConfirmed += json.getInt("confirmedSegments"); - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - projectSegments += fileSegments; - projectTranslatedSegments += fileTranslated; - projectUntranslatedSegments += fileUntranslated; - projectConfirmedSegments += fileConfirmed; - - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - writeString(out, "
#DocumentNot TranslatedTranslatedNot ConfirmedConfirmedTotal
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + fileUntranslated + "" + fileTranslated + "" + (fileSegments - fileConfirmed) + "" + fileConfirmed + "" + fileSegments + "
 Total" + projectUntranslatedSegments + "" + projectTranslatedSegments + "" + (projectSegments - projectConfirmedSegments) + "" + projectConfirmedSegments + "" + projectSegments + "
\n"); - - writeString(out, "

Words

\n"); - - count = 1; - int projectWords = 0; - int projectTranslated = 0; - int projectUntranslated = 0; - int projectConfirmed = 0; - - writeString(out, "\n"); - writeString(out, - "\n"); - it = files.iterator(); - while (it.hasNext()) { - String fileName = it.next(); - Set set = filesMap.get(fileName); - Iterator st = set.iterator(); - int fileWords = 0; - int fileUntranslated = 0; - int fileTranslated = 0; - int fileConfirmed = 0; - while (st.hasNext()) { - String key = st.next(); - JSONObject json = map.get(key); - fileWords += json.getInt("words"); - fileTranslated += json.getInt("translated"); - fileUntranslated += json.getInt("untranslated"); - fileConfirmed += json.getInt("confirmed"); - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - projectWords += fileWords; - projectTranslated += fileTranslated; - projectUntranslated += fileUntranslated; - projectConfirmed += fileConfirmed; - } - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, ""); - writeString(out, "\n"); - writeString(out, "
#DocumentNot TranslatedTranslatedNot ConfirmedConfirmedTotal
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + fileUntranslated + "" + fileTranslated + "" + (fileWords - fileConfirmed) + "" + fileConfirmed + "" + fileWords + "
 Total" + projectUntranslated + "" + projectTranslated + "" + (projectWords - projectConfirmed) + "" + projectConfirmed + "" + projectWords + "
\n"); - - SvgStats svgStats = new SvgStats(); - svgStats.analyse(xliffFile, catalog); - - Element matchesSvg = svgStats.generateMatchesSvg(); - Element translatedSvg = svgStats.generateTranslatedSvg(); - Element approvedSvg = svgStats.generateApprovedSvg(); - - writeString(out, "

Translated Segments

\n"); - writeString(out, translatedSvg.toString()); - writeString(out, "\n
\n"); - - writeString(out, "

Approved Segments

\n"); - writeString(out, approvedSvg.toString()); - writeString(out, "\n
\n"); - - writeString(out, "

TM Matches Quality

\n"); - writeString(out, matchesSvg.toString()); - writeString(out, "\n
\n"); - - writeString(out, "\n"); - writeString(out, "\n"); - } - return log.getAbsolutePath(); - } - - public void replaceText(JSONObject json) - throws SQLException, SAXException, IOException, ParserConfigurationException { - String searchText = json.getString("searchText"); - String replaceText = json.getString("replaceText"); - boolean isRegExp = json.getBoolean("regExp"); - boolean caseSensitive = json.getBoolean("caseSensitive"); - StringBuilder queryBuilder = new StringBuilder(); - queryBuilder.append("SELECT file, unitId, segId, target FROM segments WHERE type='S' AND "); - if (isRegExp) { - try { - Pattern.compile(searchText); - } catch (PatternSyntaxException e) { - throw new IOException("Invalid regular expression"); - } - queryBuilder.append("REGEXP_LIKE(targetText, '"); - queryBuilder.append(searchText); - queryBuilder.append(caseSensitive ? "', 'c')" : "', 'i')"); - } else { - queryBuilder.append(caseSensitive ? "targetText LIKE '%" : "targetText ILIKE '%"); - queryBuilder.append(escape(searchText)); - queryBuilder.append("%'"); - } - queryBuilder.append(" AND translate='Y'"); - try (ResultSet rs = stmt.executeQuery(queryBuilder.toString())) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String tgt = TMUtils.getString(rs.getNCharacterStream(4)); - - Element target = XliffUtils.buildElement(tgt); - target = replaceText(target, searchText, replaceText, isRegExp); - String pureTarget = XliffUtils.pureText(target); - updateTarget(file, unit, segment, target, pureTarget, false); - } - } - } - - private String escape(String string) { - return string.replace("'", "''").replace("%", "\\%").replace("_", "\\_"); - } - - private Element replaceText(Element target, String searchText, String replaceText, boolean isRegExp) { - List newContent = new Vector<>(); - List content = target.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.TEXT_NODE) { - String text = ((TextNode) node).getText(); - text = isRegExp ? text.replaceAll(searchText, replaceText) : text.replace(searchText, replaceText); - newContent.add(new TextNode(text)); - } - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("mrk".equals(e.getName()) || "g".equals(e.getName())) { - e = replaceText(e, searchText, replaceText, isRegExp); - } - newContent.add(e); - } - } - target.setContent(newContent); - return target; - } - - public void applyMtAll(String projectId) - throws SQLException, SAXException, IOException, ParserConfigurationException { - File projectFolder = new File(TmsServer.getProjectsFolder(), projectId); - try (FileOutputStream out = new FileOutputStream(new File(projectFolder, "applymt.xlf"))) { - String oldFile = ""; - String oldUnit = ""; - out.write(("\n") - .getBytes(StandardCharsets.UTF_8)); - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String sourceText = TMUtils.getString(rs.getNCharacterStream(4)); - - Element matchSource = XliffUtils.buildElement(sourceText); - - if (!oldFile.equals(file)) { - if (!oldFile.isEmpty()) { - out.write("\n".getBytes(StandardCharsets.UTF_8)); - } - oldFile = file; - out.write(("\n").getBytes(StandardCharsets.UTF_8)); - } - if (!oldUnit.equals(unit)) { - if (!oldUnit.isEmpty()) { - out.write("\n".getBytes(StandardCharsets.UTF_8)); - } - oldUnit = unit; - out.write(("\n").getBytes(StandardCharsets.UTF_8)); - } - Element seg = new Element("segment"); - seg.setAttribute("id", segment); - seg.addContent(matchSource); - out.write(seg.toString().getBytes(StandardCharsets.UTF_8)); - out.write("\n".getBytes(StandardCharsets.UTF_8)); - } - out.write("\n".getBytes(StandardCharsets.UTF_8)); - out.write("\n".getBytes(StandardCharsets.UTF_8)); - out.write(("").getBytes(StandardCharsets.UTF_8)); - } - } - } - - public void acceptAllMT() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; - try (ResultSet rs = stmt.executeQuery(sql)) { - try (PreparedStatement mtMatches = conn.prepareStatement( - "SELECT target FROM matches WHERE file=? AND unitId=? AND segId=? AND type='mt' LIMIT 1")) { - while (rs.next()) { - String file = rs.getString(1); - String unit = rs.getString(2); - String segment = rs.getString(3); - String src = TMUtils.getString(rs.getNCharacterStream(4)); - - mtMatches.setString(1, file); - mtMatches.setString(2, unit); - mtMatches.setString(3, segment); - try (ResultSet rs2 = mtMatches.executeQuery()) { - while (rs2.next()) { - Element source = XliffUtils.buildElement(src); - String tgt = TMUtils.getString(rs2.getNCharacterStream(1)); - Element target = XliffUtils.buildElement(tgt); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - String pureTarget = XliffUtils.pureText(target); - updateTarget(file, unit, segment, target, pureTarget, false); - } - } - } - } - } - } - - public void removeMatches(String type) throws SQLException { - try (PreparedStatement prep = conn.prepareStatement("DELETE FROM matches WHERE type=?")) { - prep.setString(1, type); - prep.execute(); - } - conn.commit(); - } - - public JSONArray getTerms(JSONObject json) throws SQLException, IOException { - JSONArray result = new JSONArray(); - getTerms.setString(1, json.getString("file")); - getTerms.setString(2, json.getString("unit")); - getTerms.setString(3, json.getString("segment")); - try (ResultSet rs = getTerms.executeQuery()) { - while (rs.next()) { - JSONObject obj = new JSONObject(); - obj.put("termId", rs.getString(1)); - obj.put("origin", rs.getString(2)); - obj.put("source", TMUtils.getString(rs.getNCharacterStream(3))); - obj.put("target", TMUtils.getString(rs.getNCharacterStream(4))); - obj.put("srcLang", srcLang); - obj.put("tgtLang", tgtLang); - result.put(obj); - } - } - return sortTerms(result); - } - - public JSONArray getSegmentTerms(JSONObject json) - throws SQLException, IOException, SAXException, ParserConfigurationException, URISyntaxException { - JSONArray result = new JSONArray(); - - getPreferences(); - int similarity = fuzzyTermSearches ? 70 : 100; - - String sourceText = ""; - getSource.setString(1, json.getString("file")); - getSource.setString(2, json.getString("unit")); - getSource.setString(3, json.getString("segment")); - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - sourceText = TMUtils.getString(rs.getNCharacterStream(2)); - } - } - Language sourceLanguage = LanguageUtils.getLanguage(srcLang); - List words = sourceLanguage.isCJK() ? cjkWordList(sourceText, NGrams.TERM_SEPARATORS) - : NGrams.buildWordList(sourceText, NGrams.TERM_SEPARATORS); - - List terms = new Vector<>(); - - String glossary = json.getString("glossary"); - GlossariesHandler.openGlossary(glossary); - String glossaryName = GlossariesHandler.getGlossaryName(glossary); - ITmEngine engine = GlossariesHandler.getEngine(glossary); - Map visited = new Hashtable<>(); - for (int i = 0; i < words.size(); i++) { - StringBuilder termBuilder = new StringBuilder(); - for (int length = 0; length < MAXTERMLENGTH; length++) { - if (i + length < words.size()) { - if (!sourceLanguage.isCJK()) { - termBuilder.append(' '); - } - termBuilder.append(words.get(i + length)); - String term = termBuilder.toString().trim(); - if (!visited.containsKey(term)) { - visited.put(term, ""); - List res = engine.searchAll(term, srcLang, similarity, caseSensitiveTermSearches); - List array = parseMatches(res, glossaryName); - for (int h = 0; h < array.size(); h++) { - Term candidate = array.get(h); - if (!terms.contains(candidate)) { - terms.add(candidate); - result.put(candidate.toJSON()); - saveTerm(json.getString("file"), json.getString("unit"), json.getString("segment"), - glossaryName, candidate.getSource(), candidate.getTarget()); - } - } - } - } - } - } - GlossariesHandler.closeGlossary(glossary); - return sortTerms(result); - } - - private JSONArray sortTerms(JSONArray array) { - if (array.length() == 0) { - return array; - } - JSONArray result = new JSONArray(); - List terms = new Vector<>(); - for (int i = 0; i < array.length(); i++) { - terms.add(new Term(array.getJSONObject(i))); - } - // sort ignoring term length - Collections.sort(terms, (Term o1, Term o2) -> o1.getSource().compareToIgnoreCase(o2.getSource())); - Iterator it = terms.iterator(); - while (it.hasNext()) { - Term term = it.next(); - result.put(term.toJSON()); - } - return result; - } - - public int getProjectTerms(String glossary) - throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { - getPreferences(); - Language sourceLanguage = LanguageUtils.getLanguage(srcLang); - int similarity = fuzzyTermSearches ? 70 : 100; - GlossariesHandler.openGlossary(glossary); - String glossaryName = GlossariesHandler.getGlossaryName(glossary); - ITmEngine engine = GlossariesHandler.getEngine(glossary); - int count = 0; - try (PreparedStatement segIterator = conn.prepareStatement( - "SELECT file, unitId, segId, sourceText FROM segments WHERE type='S' AND translate='Y' ")) { - try (ResultSet set = segIterator.executeQuery()) { - while (set.next()) { - String file = set.getString(1); - String unit = set.getString(2); - String segment = set.getString(3); - String sourceText = TMUtils.getString(set.getNCharacterStream(4)); - List words = sourceLanguage.isCJK() ? cjkWordList(sourceText, NGrams.TERM_SEPARATORS) - : NGrams.buildWordList(sourceText, NGrams.TERM_SEPARATORS); - Map visited = new Hashtable<>(); - boolean added = false; - for (int i = 0; i < words.size(); i++) { - StringBuilder termBuilder = new StringBuilder(); - for (int length = 0; length < MAXTERMLENGTH; length++) { - if (i + length < words.size()) { - if (!sourceLanguage.isCJK()) { - termBuilder.append(' '); - } - termBuilder.append(words.get(i + length)); - String term = termBuilder.toString().trim(); - if (!visited.containsKey(term)) { - visited.put(term, ""); - List res = engine.searchAll(term, srcLang, similarity, - caseSensitiveTermSearches); - List array = parseMatches(res, glossaryName); - for (int h = 0; h < array.size(); h++) { - Term candidate = array.get(h); - saveTerm(file, unit, segment, glossaryName, candidate.getSource(), - candidate.getTarget()); - added = true; - } - } - } - } - } - if (added) { - count++; - } - } - } - } - GlossariesHandler.closeGlossary(glossary); - return count; - } - - private void saveTerm(String file, String unit, String segment, String origin, String source, String target) - throws SQLException { - boolean found = false; - checkTerm.setString(1, file); - checkTerm.setString(2, unit); - checkTerm.setString(3, segment); - checkTerm.setString(4, "" + (source + origin).hashCode()); - try (ResultSet rs = checkTerm.executeQuery()) { - while (rs.next()) { - found = true; - } - } - if (!found) { - insertTerm.setString(1, file); - insertTerm.setString(2, unit); - insertTerm.setString(3, segment); - insertTerm.setString(4, "" + (source + origin).hashCode()); - insertTerm.setString(5, origin); - insertTerm.setNCharacterStream(6, new StringReader(source)); - insertTerm.setNCharacterStream(7, new StringReader(target)); - insertTerm.execute(); - conn.commit(); - } - } - - private List parseMatches(List matches, String glossaryName) { - List result = new Vector<>(); - for (int i = 0; i < matches.size(); i++) { - Map map = new Hashtable<>(); - Element tu = matches.get(i); - List tuvs = tu.getChildren("tuv"); - Iterator it = tuvs.iterator(); - while (it.hasNext()) { - Element tuv = it.next(); - map.put(tuv.getAttributeValue("xml:lang"), MemoriesHandler.pureText(tuv.getChild("seg"))); - } - if (map.containsKey(tgtLang)) { - Term term = new Term(map.get(srcLang), map.get(tgtLang), srcLang, tgtLang, glossaryName); - result.add(term); - } - } - return result; - } - - public void lockSegment(JSONObject json) throws SQLException { - String sql = "SELECT translate FROM segments WHERE file=? AND unitId=? AND segId=?"; - String segTranslate = ""; - try (PreparedStatement st = conn.prepareStatement(sql)) { - st.setString(1, json.getString("file")); - st.setString(2, json.getString("unit")); - st.setString(3, json.getString("segment")); - try (ResultSet rs = st.executeQuery()) { - while (rs.next()) { - segTranslate = rs.getString(1); - } - } - } - sql = "UPDATE segments SET translate=? WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement st = conn.prepareStatement(sql)) { - st.setString(1, segTranslate.equals("Y") ? "N" : "Y"); - st.setString(2, json.getString("file")); - st.setString(3, json.getString("unit")); - st.setString(4, json.getString("segment")); - st.executeUpdate(); - conn.commit(); - } - } - - public void unlockAll() throws SQLException { - stmt.executeUpdate("UPDATE segments SET translate='Y' WHERE type='S' AND translate='N' "); - conn.commit(); - } - - public void lockDuplicates() throws SQLException, SAXException, IOException, ParserConfigurationException { - String sql = "UPDATE segments SET translate='N' WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement lockStmt = conn.prepareStatement(sql)) { - Element currentSource = new Element("source"); - sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' ORDER BY source, file, unitId, segId"; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - Element source = XliffUtils.buildElement(TMUtils.getString(rs.getNCharacterStream(4))); - if (source.equals(currentSource)) { - lockStmt.setString(1, rs.getString(1)); - lockStmt.setString(2, rs.getString(2)); - lockStmt.setString(3, rs.getString(3)); - lockStmt.executeUpdate(); - conn.commit(); - } else { - currentSource = source; - } - } - } - } - } - - public JSONObject analyzeSpaces() throws SQLException, IOException { - getPreferences(); - JSONObject result = new JSONObject(); - JSONArray errors = new JSONArray(); - int idx = 0; - String sql = "SELECT file, unitId, segId, child, sourceText, targetText, state, translate FROM segments WHERE type='S' ORDER BY file, child "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - idx++; - boolean segTranslate = rs.getString(8).equals("Y"); - String segState = rs.getString(7); - if (!segTranslate || Constants.INITIAL.equals(segState)) { - continue; - } - String sourceText = TMUtils.getString(rs.getNCharacterStream(5)); - String targetText = TMUtils.getString(rs.getNCharacterStream(6)); - int[] sourceSpaces = countSpaces(sourceText); - int[] targetSpaces = countSpaces(targetText); - boolean initial = sourceSpaces[0] != targetSpaces[0]; - boolean trailing = sourceSpaces[1] != targetSpaces[1]; - if (initial || trailing) { - JSONObject error = new JSONObject(); - error.put("file", rs.getString(1)); - error.put("unit", rs.getString(2)); - error.put("segment", rs.getString(3)); - String type = ""; - if (initial) { - type = "Initial"; - } - if (trailing) { - type = "Trailing"; - } - if (initial && trailing) { - type = "Initial - Trailing"; - } - error.put("type", type); - error.put("index", idx); - errors.put(error); - } - } - } - result.put("errors", errors); - return result; - } - - private int[] countSpaces(String text) { - int start = 0; - for (int i = 0; i < text.length(); i++) { - char c = text.charAt(i); - if (!Character.isWhitespace(c)) { - break; - } - start++; - } - int end = 0; - for (int i = text.length() - 1; i >= 0; i--) { - char c = text.charAt(i); - if (!Character.isWhitespace(c)) { - break; - } - end++; - } - return new int[] { start, end }; - } - - public JSONObject analyzeTags() throws SQLException, SAXException, IOException, ParserConfigurationException { - getPreferences(); - JSONObject result = new JSONObject(); - JSONArray errors = new JSONArray(); - int idx = 0; - String sql = "SELECT file, unitId, segId, child, source, target, state, translate FROM segments WHERE type='S' ORDER BY file, child "; - try (ResultSet rs = stmt.executeQuery(sql)) { - while (rs.next()) { - idx++; - boolean segTranslate = rs.getString(8).equals("Y"); - String segState = rs.getString(7); - if (!segTranslate || Constants.INITIAL.equals(segState)) { - continue; - } - String sourceText = TMUtils.getString(rs.getNCharacterStream(5)); - Element source = XliffUtils.buildElement(sourceText); - String targetText = TMUtils.getString(rs.getNCharacterStream(6)); - Element target = XliffUtils.buildElement(targetText); - - List sourceTags = tagsList(source); - List targetTags = tagsList(target); - if (sourceTags.size() > targetTags.size()) { - JSONObject error = new JSONObject(); - error.put("file", rs.getString(1)); - error.put("unit", rs.getString(2)); - error.put("segment", rs.getString(3)); - error.put("type", "Missing Tags"); - error.put("index", idx); - errors.put(error); - continue; - } - if (sourceTags.size() < targetTags.size()) { - JSONObject error = new JSONObject(); - error.put("file", rs.getString(1)); - error.put("unit", rs.getString(2)); - error.put("segment", rs.getString(3)); - error.put("type", "Extra Tags"); - error.put("index", idx); - errors.put(error); - continue; - } - if (sourceTags.size() == targetTags.size()) { - boolean skip = false; - for (int i = 0; i < sourceTags.size(); i++) { - String srcTag = sourceTags.get(i); - boolean found = false; - for (int j = 0; j < targetTags.size(); j++) { - String tgtTag = targetTags.get(j); - if (srcTag.equals(tgtTag)) { - found = true; - break; - } - } - if (!found) { - JSONObject error = new JSONObject(); - error.put("file", rs.getString(1)); - error.put("unit", rs.getString(2)); - error.put("segment", rs.getString(3)); - error.put("type", "Different Tag"); - error.put("index", idx); - errors.put(error); - skip = true; - } - } - if (!skip) { - for (int i = 0; i < sourceTags.size(); i++) { - if (!sourceTags.get(i).equals(targetTags.get(i))) { - JSONObject error = new JSONObject(); - error.put("file", rs.getString(1)); - error.put("unit", rs.getString(2)); - error.put("segment", rs.getString(3)); - error.put("type", "Tags in wrong order"); - error.put("index", idx); - errors.put(error); - break; - } - } - } - } - } - } - result.put("errors", errors); - return result; - } - - private List tagsList(Element root) { - List result = new Vector<>(); - List content = root.getContent(); - Iterator it = content.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("mrk".equals(e.getName()) || "pc".equals(e.getName())) { - result.add(XliffUtils.getHeader(e)); - result.add(XliffUtils.getTail(e)); - } else { - result.add(e.toString()); - } - } - } - return result; - } - - public static List cjkWordList(String string, String separator) { - List result = new Vector<>(); - StringBuilder word = new StringBuilder(); - for (int i = 0; i < string.length(); i++) { - char c = string.charAt(i); - if (Character.isIdeographic(c)) { - if (word.length() != 0) { - result.add(word.toString()); - word.setLength(0); - } - result.add("" + c); - continue; - } - if (separator.indexOf(c) != -1) { - if (word.length() != 0) { - result.add(word.toString()); - word.setLength(0); - } - } else { - word.append(c); - } - } - if (word.length() != 0) { - result.add(word.toString()); - } - return result; - } - - public String exportHTML(String title) - throws SQLException, IOException, SAXException, ParserConfigurationException { - File output = new File(xliffFile + ".html"); - try (FileOutputStream out = new FileOutputStream(output)) { - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "" + XMLUtils.cleanText(title) + "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "

" + XMLUtils.cleanText(title) + "

\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, "\n"); - - String sourceDir = LanguageUtils.isBiDi(srcLang) ? " dir=\"rtl\"" : ""; - String targetDir = LanguageUtils.isBiDi(tgtLang) ? " dir=\"rtl\"" : ""; - - try (ResultSet rs = stmt.executeQuery( - "SELECT source, target, state, space, translate, file, child FROM segments WHERE type='S' ORDER BY file, child")) { - int count = 1; - JSONObject tagsData = new JSONObject(); - while (rs.next()) { - String src = TMUtils.getString(rs.getNCharacterStream(1)); - String tgt = TMUtils.getString(rs.getNCharacterStream(2)); - String segState = rs.getString(3); - boolean segPreserve = "Y".equals(rs.getString(4)); - boolean locked = "N".equals(rs.getString(5)); - Element source = XliffUtils.buildElement(src); - Element target = XliffUtils.buildElement(tgt); - String box = SVG_BLANK; - String border = "grey"; - if (segState.equals("translated")) { - border = "orange"; - box = SVG_TRANSLATED; - } - if (segState.equals("final")) { - border = "green"; - box = SVG_FINAL; - } - if (locked) { - box = SVG_LOCK; - } - String space = segPreserve ? "preserve" : ""; - tagsMap = new Hashtable<>(); - tag = 1; - - writeString(out, "\n"); - writeString(out, "\n"); - writeString(out, - "\n"); - writeString(out, "\n"); - writeString(out, - "\n"); - writeString(out, "\n"); - } - } - writeString(out, "
#" + LanguageUtils.getLanguage(srcLang).toString() + "" + SVG_BLANK + "" + LanguageUtils.getLanguage(tgtLang).toString() + "
" + count++ + "" - + XliffUtils.highlightSpaces( - removeSvg(addHtmlTags(source, "", false, false, tagsData, segPreserve))) - + " " + box + "" - + XliffUtils.highlightSpaces( - removeSvg(addHtmlTags(target, "", false, false, tagsData, segPreserve))) - + "
\n"); - writeString(out, "\n"); - writeString(out, ""); - } - return output.getAbsolutePath(); - } - - private static void writeString(FileOutputStream out, String string) throws IOException { - out.write(string.getBytes(StandardCharsets.UTF_8)); - } - - private static String removeSvg(String segment) { - if (segment.isEmpty()) { - return segment; - } - int index = segment.indexOf("", index) + 1; - String start = segment.substring(0, index); - String img = segment.substring(index, end); - String tag = "" + parseImg(img) + ""; - String rest = segment.substring(end); - segment = start + tag + rest; - index = segment.indexOf(" files = document.getRootElement().getChildren("file"); - for (int i = 0; i < files.size(); i++) { - Element file = files.get(i); - if (file.getAttributeValue("id").equals(currentFile)) { - List units = file.getChildren("unit"); - for (int j = 0; j < units.size(); j++) { - unit = units.get(j); - if (unit.getAttributeValue("id").equals(currentUnit)) { - List segments = unit.getChildren("segment"); - for (int k = 0; k < segments.size(); k++) { - segment = segments.get(k); - if (segment.getAttributeValue("id").equals(segmentId)) { - break; - } - } - break; - } - } - break; - } - } - - List segs = unit.getChildren("segment"); - Iterator tt = segs.iterator(); - while (tt.hasNext()) { - Element seg = tt.next(); - seg.removeChild("target"); - Element target = getTarget(currentFile, currentUnit, seg.getAttributeValue("id")); - seg.setAttribute("state", getState(currentFile, currentUnit, seg.getAttributeValue("id"))); - seg.addContent(target); - } - - Element oldSource = segment.getChild("source"); - List list1 = new Vector<>(); - List list2 = new Vector<>(); - List currentList = list1; - int visited = 0; - List sourceContent = oldSource.getContent(); - Iterator it = sourceContent.iterator(); - while (it.hasNext()) { - XMLNode node = it.next(); - if (node.getNodeType() == XMLNode.TEXT_NODE) { - TextNode textNode = (TextNode) node; - String text = textNode.getText(); - int length = text.length(); - if (length >= offset - visited && offset - visited > 0) { - String left = text.substring(0, offset - visited); - String right = text.substring(offset - visited); - currentList.add(new TextNode(left)); - currentList = list2; - currentList.add(new TextNode(right)); - } else { - currentList.add(node); - } - visited = visited + length; - } - if (node.getNodeType() == XMLNode.ELEMENT_NODE) { - Element e = (Element) node; - if ("mrk".equals(e.getName())) { - String text = e.getText(); - int length = text.length(); - if (length >= offset - visited && offset - visited > 0) { - throw new IOException("Can't split segment in locked text section."); - } else { - visited = visited + length; - } - } - currentList.add(node); - } - } - - Element oldTarget = getTarget(currentFile, currentUnit, segmentId); - String pureTarget = XliffUtils.pureText(oldTarget); - - Element source1 = new Element("source"); - source1.setAttribute("xml:space", oldSource.getAttributeValue("xml:space", "default")); - source1.setContent(list1); - - Element segment1 = new Element("segment"); - segment1.setAttribute("id", segmentId + "-1"); - segment1.setAttribute("state", pureTarget.isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); - segment1.addContent(source1); - segment1.addContent(oldTarget); - - Element source2 = new Element("source"); - source2.setAttribute("xml:space", oldSource.getAttributeValue("xml:space", "default")); - source2.setContent(list2); - - Element target2 = new Element("target"); - if (oldSource.hasAttribute("xml:space")) { - target2.setAttribute("xml:space", oldSource.getAttributeValue("xml:space")); - } - - Element segment2 = new Element("segment"); - segment2.setAttribute("id", segmentId + "-2"); - segment2.setAttribute("state", Constants.INITIAL); - segment2.addContent(source2); - segment2.addContent(target2); - - List oldContent = unit.getChildren(); - List newContent = new Vector<>(); - - unit.removeChild("mtc:matches"); - unit.removeChild("gls:glossary"); - - Iterator ot = oldContent.iterator(); - while (ot.hasNext()) { - Element child = ot.next(); - if ("segment".equals(child.getName()) && segmentId.equals(child.getAttributeValue("id"))) { - newContent.add(segment1); - newContent.add(segment2); - } else { - newContent.add(child); - } - } - unit.setContent(newContent); - Indenter.indent(unit, 2); - - String sql = "SELECT MIN(child) FROM segments WHERE file=? AND unitId=?"; - index = 0; - - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, currentFile); - prep.setString(2, currentUnit); - try (ResultSet rs = prep.executeQuery()) { - while (rs.next()) { - index = rs.getInt(1); - } - } - } - - deleteUnitSegments(currentFile, currentUnit); - - sql = "UPDATE segments SET child = child + 1 WHERE file = '" + currentFile + "' AND child >= " + index; - stmt.execute(sql); - - sql = "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; - insertSegmentStmt = conn.prepareStatement(sql); - - List segments = unit.getChildren(); - for (int i = 0; i < segments.size(); i++) { - Element e = segments.get(i); - if ("segment".equals(e.getName())) { - String id = e.getAttributeValue("id"); - Element source = e.getChild("source"); - boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); - Element target = e.getChild("target"); - state = e.getAttributeValue("state", - XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); - preserve = preserve || sourcePreserve - || "preserve".equals(target.getAttributeValue("xml:space", "default")); - - insertSegment(currentFile, currentUnit, id, "S", true, source, target); - } - if ("ignorable".equals(e.getName())) { - String id = e.getAttributeValue("id"); - state = ""; - insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); - } - } - insertSegmentStmt.close(); - conn.commit(); - indexSegments(); - saveXliff(); - } - - private void deleteUnitSegments(String file, String unit) throws SQLException { - String sql = "DELETE FROM segments WHERE file=? AND unitId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.execute(); - } - } - - private void deleteSegment(String file, String unit, String segment) throws SQLException { - String sql = "DELETE FROM segments WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segment); - prep.execute(); - } - - sql = "DELETE FROM matches WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segment); - prep.execute(); - } - - sql = "DELETE FROM terms WHERE file=? AND unitId=? AND segId=?"; - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, file); - prep.setString(2, unit); - prep.setString(3, segment); - prep.execute(); - } - } - - public void mergeSegment(JSONObject json) - throws SAXException, IOException, ParserConfigurationException, SQLException { - - currentFile = json.getString("file"); - currentUnit = json.getString("unit"); - String segmentId = json.getString("segment"); - - Element unit = null; - Element segment = null; - document = builder.build(xliffFile); - List files = document.getRootElement().getChildren("file"); - for (int i = 0; i < files.size(); i++) { - Element file = files.get(i); - if (file.getAttributeValue("id").equals(currentFile)) { - List units = file.getChildren("unit"); - for (int j = 0; j < units.size(); j++) { - unit = units.get(j); - if (unit.getAttributeValue("id").equals(currentUnit)) { - List segments = unit.getChildren("segment"); - for (int k = 0; k < segments.size(); k++) { - segment = segments.get(k); - if (segment.getAttributeValue("id").equals(segmentId)) { - break; - } - } - break; - } - } - break; - } - } - - List segs = unit.getChildren("segment"); - Iterator tt = segs.iterator(); - while (tt.hasNext()) { - Element seg = tt.next(); - seg.removeChild("target"); - Element target = getTarget(currentFile, currentUnit, seg.getAttributeValue("id")); - seg.setAttribute("state", getState(currentFile, currentUnit, seg.getAttributeValue("id"))); - seg.addContent(target); - } - - String sql = "SELECT MIN(child) FROM segments WHERE file=? AND unitId=?"; - index = 0; - - try (PreparedStatement prep = conn.prepareStatement(sql)) { - prep.setString(1, currentFile); - prep.setString(2, currentUnit); - try (ResultSet rs = prep.executeQuery()) { - while (rs.next()) { - index = rs.getInt(1); - } - } - } - - List oldContent = unit.getChildren(); - List newContent = new Vector<>(); - - String deletedId = null; - boolean adding = false; - Iterator ot = oldContent.iterator(); - while (ot.hasNext()) { - Element child = ot.next(); - if ("segment".equals(child.getName())) { - if (!adding) { - newContent.add(child); - } else { - segment.getChild("source").addContent(child.getChild("source").getContent()); - segment.getChild("target").addContent(child.getChild("target").getContent()); - deletedId = child.getAttributeValue("id"); - deleteSegment(currentFile, currentUnit, deletedId); - adding = false; - } - if (segmentId.equals(child.getAttributeValue("id"))) { - adding = true; - } - } else if ("ignorable".equals(child.getName())) { - if (!adding) { - newContent.add(child); - } else { - segment.getChild("source").addContent(child.getChild("source").getContent()); - Element target = child.getChild("target"); - if (target != null) { - segment.getChild("target").addContent(target.getContent()); - } - } - } else { - newContent.add(child); - } - } - unit.setContent(newContent); - if (deletedId != null) { - Element matches = unit.getChild("mtc:matches"); - if (matches != null) { - Iterator it = matches.getChildren().iterator(); - while (it.hasNext()) { - Element match = it.next(); - if (match.getAttributeValue("ref").equals("#" + deletedId)) { - match.setAttribute("ref", "#" + deletedId); - } - } - } - Element terms = unit.getChild("gls:glossary"); - if (terms != null) { - Iterator it = terms.getChildren().iterator(); - while (it.hasNext()) { - Element term = it.next(); - if (term.getAttributeValue("ref").equals("#" + deletedId)) { - term.setAttribute("ref", "#" + deletedId); - } - } - } - } - Indenter.indent(unit, 2); - - deleteUnitSegments(currentFile, currentUnit); - - sql = "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; - insertSegmentStmt = conn.prepareStatement(sql); - - List segments = unit.getChildren(); - for (int i = 0; i < segments.size(); i++) { - Element e = segments.get(i); - if ("segment".equals(e.getName())) { - String id = e.getAttributeValue("id"); - Element source = e.getChild("source"); - boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); - Element target = e.getChild("target"); - if (target == null) { - target = new Element("target"); - if (source.hasAttribute("xml:space")) { - target.setAttribute("xml:space", source.getAttributeValue("xml:space")); - } - } - state = e.getAttributeValue("state", - XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); - preserve = preserve || sourcePreserve - || "preserve".equals(target.getAttributeValue("xml:space", "default")); - - insertSegment(currentFile, currentUnit, id, "S", true, source, target); - } - if ("ignorable".equals(e.getName())) { - String id = e.getAttributeValue("id"); - state = ""; - insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); - } - } - - insertSegmentStmt.close(); - conn.commit(); - indexSegments(); - saveXliff(); - } - - private Element getTarget(String file, String unit, String segment) - throws SQLException, SAXException, IOException, ParserConfigurationException { - getTargetStmt.setString(1, file); - getTargetStmt.setString(2, unit); - getTargetStmt.setString(3, segment); - String tgt = ""; - try (ResultSet rs = getTargetStmt.executeQuery()) { - while (rs.next()) { - tgt = TMUtils.getString(rs.getNCharacterStream(1)); - state = rs.getString(2); - } - } - if (tgt == null || tgt.isEmpty()) { - return new Element("target"); - } - return XliffUtils.buildElement(tgt); - } - - private String getState(String file, String unit, String segment) throws SQLException { - getTargetStmt.setString(1, file); - getTargetStmt.setString(2, unit); - getTargetStmt.setString(3, segment); - String result = ""; - try (ResultSet rs = getTargetStmt.executeQuery()) { - while (rs.next()) { - result = rs.getString(2); - } - } - return result; - } - - private boolean isTranslatable(String file, String unit, String segment) throws SQLException { - getSource.setString(1, file); - getSource.setString(2, unit); - getSource.setString(3, segment); - boolean translate = true; - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - translate = rs.getString(4).equals("Y"); - } - } - return translate; - } - - private void indexSegments() throws SQLException { - int idx = 0; - String update = "UPDATE segments SET idx=? WHERE file=? AND unitID=? AND segId=?"; - try (PreparedStatement prep = conn.prepareStatement(update)) { - try (Statement st = conn.createStatement()) { - String sql = "SELECT file, unitId, segId, child FROM segments WHERE type='S' ORDER BY file, child"; - try (ResultSet rs = st.executeQuery(sql)) { - while (rs.next()) { - prep.setInt(1, idx++); - prep.setString(2, rs.getString(1)); - prep.setString(3, rs.getString(2)); - prep.setString(4, rs.getString(3)); - prep.executeUpdate(); - } - } - } - } - conn.commit(); - } - - public JSONObject getSegmentSource(JSONObject json) - throws JSONException, SQLException, SAXException, IOException, ParserConfigurationException { - getSource.setString(1, json.getString("file")); - getSource.setString(2, json.getString("unit")); - getSource.setString(3, json.getString("segment")); - String src = ""; - try (ResultSet rs = getSource.executeQuery()) { - while (rs.next()) { - src = TMUtils.getString(rs.getNCharacterStream(1)); - } - } - Element source = XliffUtils.buildElement(src); - String plainText = XliffUtils.pureText(source); - JSONObject result = new JSONObject(); - result.put("source", source.toString()); - result.put("plainText", "" + plainText + ""); - return result; - } - - public void setMTMatches(JSONObject json) - throws SQLException, IOException, JSONException, SAXException, ParserConfigurationException { - String file = json.getString("file"); - String unit = json.getString("unit"); - String segment = json.getString("segment"); - JSONArray translations = json.getJSONArray("translations"); - for (int i = 0; i < translations.length(); i++) { - JSONObject translation = translations.getJSONObject(i); - Element source = XliffUtils.buildElement(translation.getString("source")); - Element target = XliffUtils.buildElement(translation.getString("target")); - String origin = translation.getString("origin"); - insertMatch(file, unit, segment, origin, Constants.MT, 0, source, target, new JSONObject()); - } - } + Logger logger = System.getLogger(XliffStore.class.getName()); + + public static final int THRESHOLD = 60; + public static final int MAXTERMLENGTH = 5; + public static final int BATCHSIZE = 100; + + public static final String SVG_BLANK = ""; + public static final String SVG_UNTRANSLATED = ""; + public static final String SVG_TRANSLATED = ""; + public static final String SVG_FINAL = ""; + public static final String SVG_LOCK = ""; + + private String xliffFile; + private SAXBuilder builder; + private Document document; + + private File database; + private Connection conn; + private PreparedStatement insertFile; + private PreparedStatement insertUnit; + private PreparedStatement insertSegmentStmt; + private PreparedStatement insertMatch; + private PreparedStatement updateMatch; + private PreparedStatement getMatches; + private PreparedStatement bestMatch; + private PreparedStatement insertTerm; + private PreparedStatement getTerms; + private PreparedStatement getUnitData; + private PreparedStatement getSource; + private PreparedStatement getTargetStmt; + private PreparedStatement updateTargetStmt; + private PreparedStatement unitMatches; + private PreparedStatement unitTerms; + private PreparedStatement unitNotes; + private PreparedStatement checkTerm; + private PreparedStatement getNotesStmt; + private PreparedStatement insertNoteStmt; + private PreparedStatement getSegment; + + private Statement stmt; + private boolean preserve; + + private static String catalog; + private static boolean acceptUnconfirmed; + private static boolean fuzzyTermSearches; + private static boolean caseSensitiveTermSearches; + private static boolean caseSensitiveMatches; + + private int index; + private int nextId; + private String currentFile; + private String currentUnit; + private String state; + private int tagCount; + + private String srcLang; + private String tgtLang; + + private int tag; + private Map tagsMap; + private Map notesMap; + + private static Pattern pattern; + private static String lastFilterText; + + public XliffStore(String xliffFile, String sourceLang, String targetLang) + throws SAXException, IOException, ParserConfigurationException, URISyntaxException, SQLException { + + this.xliffFile = xliffFile; + srcLang = sourceLang; + tgtLang = targetLang; + + File xliff = new File(xliffFile); + + if (new File(xliff.getParentFile(), "h2data").exists()) { + MessageFormat mf = new MessageFormat(Messages.getString("XliffStore.0")); + throw new IOException(mf.format(new String[] { xliff.getParentFile().getName() })); + } + + database = new File(xliff.getParentFile(), "sqlite"); + boolean needsLoading = !database.exists(); + if (!database.exists()) { + database.mkdirs(); + } + getPreferences(); + builder = new SAXBuilder(); + builder.setEntityResolver(new Catalog(catalog)); + + DriverManager.registerDriver(new org.sqlite.JDBC()); + conn = DriverManager + .getConnection("jdbc:sqlite:" + database.getAbsolutePath().replace('\\', '/') + "/database.db"); + conn.setAutoCommit(false); + Function.create(conn, "REGEXP", new Function() { + @Override + protected void xFunc() throws SQLException { + String expression = value_text(0); + String value = value_text(1); + if (value == null) + value = ""; + + Pattern pat = Pattern.compile(expression); + result(pat.matcher(value).find() ? 1 : 0); + } + }); + if (needsLoading) { + if (TmsServer.isDebug()) { + logger.log(Level.INFO, Messages.getString("XliffStore.1")); + } + createTables(); + } + + getUnitData = conn.prepareStatement("SELECT data, compressed FROM units WHERE file=? AND unitId=?"); + getSource = conn.prepareStatement( + "SELECT source, sourceText, state, translate FROM segments WHERE file=? AND unitId=? AND segId=?"); + getTargetStmt = conn + .prepareStatement("SELECT target, state FROM segments WHERE file=? AND unitId=? AND segId=?"); + updateTargetStmt = conn.prepareStatement( + "UPDATE segments SET target=?, targetText=?, state=? WHERE file=? AND unitId=? AND segId=?"); + insertMatch = conn.prepareStatement( + "INSERT INTO matches (file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed) VALUES(?,?,?,?,?,?,?,?,?,?,?)"); + updateMatch = conn.prepareStatement( + "UPDATE matches SET origin=?, type=?, similarity=?, source=?, target=?, data=?, compressed=? WHERE file=? AND unitId=? AND segId=? AND matchId=?"); + getMatches = conn.prepareStatement( + "SELECT file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed FROM matches WHERE file=? AND unitId=? AND segId=? ORDER BY similarity DESC"); + bestMatch = conn.prepareStatement( + "SELECT type, similarity FROM matches WHERE file=? AND unitId=? AND segId=? ORDER BY similarity DESC LIMIT 1"); + insertTerm = conn.prepareStatement( + "INSERT INTO terms (file, unitId, segId, termid, origin, source, target) VALUES(?,?,?,?,?,?,?)"); + getTerms = conn.prepareStatement( + "SELECT termid, origin, source, target FROM terms WHERE file=? AND unitId=? AND segId=? ORDER BY source"); + checkTerm = conn + .prepareStatement("SELECT target FROM terms WHERE file=? AND unitId=? AND segId=? AND termid=?"); + getNotesStmt = conn.prepareStatement("SELECT noteId, note FROM notes WHERE file=? AND unitId=? AND segId=?"); + getSegment = conn.prepareStatement("SELECT source, target FROM segments WHERE file=? AND unitId=? AND segId=?"); + stmt = conn.createStatement(); + if (needsLoading) { + document = builder.build(xliffFile); + parseDocument(); + conn.commit(); + indexSegments(); + } + } + + private void createTables() throws SQLException { + String files = """ + CREATE TABLE files ( + id VARCHAR(50) NOT NULL, + name VARCHAR(350) NOT NULL, + PRIMARY KEY(id, name) + );"""; + String units = """ + CREATE TABLE units ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + data TEXT NOT NULL, + compressed CHAR(1) NOT NULL DEFAULT 'N', + PRIMARY KEY(file, unitId) + );"""; + String segments = """ + CREATE TABLE segments ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + type CHAR(1) NOT NULL DEFAULT 'S', + state VARCHAR(12) DEFAULT 'initial', + child INTEGER, + translate CHAR(1), + tags INTEGER DEFAULT 0, + space CHAR(1) DEFAULT 'N', + source TEXT NOT NULL, + sourceText TEXT NOT NULL, + target TEXT NOT NULL, + targetText TEXT NOT NULL, + words INTEGER NOT NULL DEFAULT 0, + idx INTEGER, + PRIMARY KEY(file, unitId, segId, type) + );"""; + String matches = """ + CREATE TABLE matches ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + matchId varchar(256), + origin VARCHAR(256), + type CHAR(2) NOT NULL DEFAULT 'tm', + similarity INTEGER DEFAULT 0, + source TEXT NOT NULL, + target TEXT NOT NULL, + data TEXT NOT NULL, + compressed CHAR(1) NOT NULL DEFAULT 'N', + PRIMARY KEY(file, unitId, segId, matchid) + );"""; + String terms = """ + CREATE TABLE terms ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + termid varchar(256), + origin VARCHAR(256), + source TEXT NOT NULL, + target TEXT NOT NULL, + PRIMARY KEY(file, unitId, segId, termid) + );"""; + String notes = """ + CREATE TABLE notes ( + file VARCHAR(50), + unitId VARCHAR(256) NOT NULL, + segId VARCHAR(256) NOT NULL, + noteid varchar(256) NOT NULL, + note TEXT NOT NULL, + PRIMARY KEY(file, unitId, segId, noteid) + );"""; + try (Statement create = conn.createStatement()) { + create.execute(files); + create.execute(units); + create.execute(segments); + create.execute(matches); + create.execute(terms); + create.execute(notes); + } + conn.commit(); + } + + private void parseDocument() throws SQLException, IOException { + insertFile = conn.prepareStatement("INSERT INTO files (id, name) VALUES (?,?)"); + insertUnit = conn.prepareStatement("INSERT INTO units (file, unitId, data, compressed) VALUES (?,?,?,?)"); + insertSegmentStmt = conn.prepareStatement( + "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"); + insertNoteStmt = conn + .prepareStatement("INSERT INTO notes (file, unitId, segId, noteId, note) values (?,?,?,?,?)"); + recurse(document.getRootElement()); + insertFile.close(); + insertUnit.close(); + insertNoteStmt.close(); + insertSegmentStmt.close(); + } + + private void recurse(Element e) throws SQLException, IOException { + if ("file".equals(e.getName())) { + currentFile = e.getAttributeValue("id"); + insertFile.setString(1, currentFile); + insertFile.setString(2, e.getAttributeValue("original")); + insertFile.execute(); + index = 0; + } + if ("unit".equals(e.getName())) { + tagCount = 0; + nextId = 0; + currentUnit = e.getAttributeValue("id"); + preserve = "preserve".equals(e.getAttributeValue("xml:space", "default")); + JSONObject data = new JSONObject(); + + Element originalData = e.getChild("originalData"); + if (originalData != null) { + List list = originalData.getChildren(); + for (int i = 0; i < list.size(); i++) { + Element d = list.get(i); + data.put(d.getAttributeValue("id"), d.getText()); + tagCount++; + } + } + + Element matches = e.getChild("mtc:matches"); + if (matches != null) { + List m = matches.getChildren("mtc:match"); + Iterator mit = m.iterator(); + while (mit.hasNext()) { + insertMatch(currentFile, currentUnit, mit.next()); + } + } + + notesMap = new Hashtable<>(); + Element notes = e.getChild("notes"); + if (notes != null) { + List n = notes.getChildren("note"); + Iterator nit = n.iterator(); + while (nit.hasNext()) { + Element note = nit.next(); + if (note.hasAttribute("mtc:ref")) { + String segId = note.getAttributeValue("mtc:ref"); + if (segId.startsWith("#")) { + segId = segId.substring(1); + } + insertNote(currentFile, currentUnit, segId, note); + } else { + notesMap.put(note.getAttributeValue("id"), note); + } + } + } + if (tagCount > 0) { + String dataString = data.toString(); + insertUnit.setString(1, currentFile); + insertUnit.setString(2, currentUnit); + insertUnit.setString(3, dataString); + insertUnit.setString(4, "N"); + insertUnit.execute(); + } + Element glossary = e.getChild("gls:glossary"); + if (glossary != null) { + List entries = glossary.getChildren("gls:glossEntry"); + Iterator it = entries.iterator(); + while (it.hasNext()) { + Element glossEntry = it.next(); + if (glossEntry.hasAttribute("ref")) { + String segId = glossEntry.getAttributeValue("ref"); + if (segId.startsWith("#")) { + segId = segId.substring(1); + } + Element term = glossEntry.getChild("gls:term"); + String source = term.getText(); + String origin = term.getAttributeValue("source"); + Element translation = glossEntry.getChild("gls:translation"); + if (translation != null) { + String target = translation.getText(); + saveTerm(currentFile, currentUnit, segId, origin, source, target); + } + } + } + } + } + if ("segment".equals(e.getName())) { + String id = e.getAttributeValue("id"); + if (id.isEmpty()) { + id = "s" + nextId++; + e.setAttribute("id", id); + } + Element source = e.getChild("source"); + boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); + Element target = e.getChild("target"); + if (target == null) { + target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + } + + List segmentNotes = new Vector<>(); + if (!notesMap.isEmpty()) { + segmentNotes.addAll(harvestNotes(source)); + source = FromXliff2.removeComments(source); + segmentNotes.addAll(harvestNotes(target)); + target = FromXliff2.removeComments(target); + } + + state = e.getAttributeValue("state", + XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); + String subState = e.getAttributeValue("subState"); + boolean translate = true; + if ("openxliff:locked".equals(subState)) { + translate = false; + } + preserve = preserve || sourcePreserve + || "preserve".equals(target.getAttributeValue("xml:space", "default")); + + insertSegment(currentFile, currentUnit, id, "S", translate, source, target); + for (int i = 0; i < segmentNotes.size(); i++) { + String noteId = segmentNotes.get(i); + Element note = notesMap.get(noteId); + insertNote(currentFile, currentUnit, id, note); + } + } + if ("ignorable".equals(e.getName())) { + String id = e.getAttributeValue("id"); + if (id.isEmpty()) { + id = "i" + nextId++; + e.setAttribute("id", id); + } + insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); + } + List children = e.getChildren(); + Iterator it = children.iterator(); + while (it.hasNext()) { + recurse(it.next()); + } + if ("file".equals(e.getName())) { + conn.commit(); + } + } + + private List harvestNotes(Element element) { + List result = new Vector<>(); + if ("mrk".equals(element.getName()) && "comment".equals(element.getAttributeValue("type"))) { + if (element.hasAttribute("ref")) { + String ref = element.getAttributeValue("ref"); + result.add(ref.substring(ref.indexOf('=') + 1)); + } + if (element.hasAttribute("value")) { + Element note = new Element("note"); + note.setText(element.getAttributeValue("value")); + String id = "" + (result.size() + 100); + note.setAttribute("id", id); + notesMap.put(id, note); + result.add(id); + } + } + List children = element.getChildren(); + Iterator it = children.iterator(); + while (it.hasNext()) { + result.addAll(harvestNotes(it.next())); + } + return result; + } + + private synchronized void insertSegment(String file, String unit, String segment, String type, boolean translate, + Element source, Element target) throws SQLException { + String pureSource = XliffUtils.pureText(source); + insertSegmentStmt.setString(1, file); + insertSegmentStmt.setString(2, unit); + insertSegmentStmt.setString(3, segment); + insertSegmentStmt.setString(4, type); + insertSegmentStmt.setString(5, state); + insertSegmentStmt.setInt(6, index++); + insertSegmentStmt.setString(7, translate ? "Y" : "N"); + insertSegmentStmt.setInt(8, tagCount); + insertSegmentStmt.setString(9, preserve ? "Y" : "N"); + insertSegmentStmt.setString(10, source.toString()); + insertSegmentStmt.setString(11, pureSource); + insertSegmentStmt.setString(12, (target != null ? target.toString() : "")); + insertSegmentStmt.setString(13, (target != null ? XliffUtils.pureText(target) : "")); + insertSegmentStmt.setInt(14, type.equals("S") ? RepetitionAnalysis.wordCount(pureSource, srcLang) : 0); + insertSegmentStmt.execute(); + } + + private void insertMatch(String file, String unit, Element match) throws SQLException { + Element originalData = match.getChild("originalData"); + Element source = match.getChild("source"); + Element target = match.getChild("target"); + JSONObject tagsData = new JSONObject(); + if (originalData != null) { + List list = originalData.getChildren(); + for (int i = 0; i < list.size(); i++) { + Element d = list.get(i); + tagsData.put(d.getAttributeValue("id"), d.getText()); + } + } + String segment = match.getAttributeValue("ref"); + if (segment.startsWith("#")) { + segment = segment.substring(1); + } + String type = match.getAttributeValue("type", Constants.TM); + String origin = match.getAttributeValue("origin"); + int similarity = Math.round(Float.parseFloat(match.getAttributeValue("matchQuality", "0.0"))); + + insertMatch(file, unit, segment, origin, type, similarity, source, target, tagsData); + } + + private void insertNote(String file, String unit, String segId, Element note) throws SQLException { + insertNoteStmt.setString(1, file); + insertNoteStmt.setString(2, unit); + insertNoteStmt.setString(3, segId); + insertNoteStmt.setString(4, note.getAttributeValue("id", unit)); + insertNoteStmt.setString(5, note.getText()); + insertNoteStmt.execute(); + } + + public int size() throws SQLException { + int count = 0; + String sql = "SELECT count(*) FROM segments WHERE type='S'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + count = rs.getInt(1); + } + } + return count; + } + + public void saveXliff() throws IOException { + XMLOutputter outputter = new XMLOutputter(); + outputter.preserveSpace(true); + Indenter.indent(document.getRootElement(), 2); + try (FileOutputStream out = new FileOutputStream(xliffFile)) { + outputter.output(document, out); + } + } + + public synchronized List getSegments(int start, int count, String filterText, String filterLanguage, + boolean caseSensitiveFilter, boolean regExp, boolean showUntranslated, boolean showTranslated, + boolean showConfirmed, String sortOption, boolean sortDesc) + throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { + List result = new Vector<>(); + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append( + "SELECT file, unitId, segId, child, source, target, tags, state, space, translate, sourceText, targetText, idx FROM segments WHERE type='S'"); + if (!filterText.isEmpty()) { + if (regExp) { + try { + Pattern.compile(filterText); + } catch (PatternSyntaxException e) { + throw new IOException("Invalid regular expression"); + } + if ("source".equals(filterLanguage)) { + queryBuilder.append(" AND sourceText REGEXP '"); + } else { + queryBuilder.append(" AND targetText REGEXP '"); + } + queryBuilder.append(filterText); + queryBuilder.append("'"); + } else { + if (caseSensitiveFilter) { + if ("source".equals(filterLanguage)) { + queryBuilder.append(" AND sourceText GLOB '*"); + } else { + queryBuilder.append(" AND targetText GLOB '*"); + } + } else { + if ("source".equals(filterLanguage)) { + queryBuilder.append(" AND sourceText LIKE '%"); + } else { + queryBuilder.append(" AND targetText LIKE '%"); + } + } + queryBuilder.append(escape(filterText)); + queryBuilder.append(caseSensitiveFilter ? "*'" : "%'"); + } + if (!showUntranslated) { + queryBuilder.append(" AND state <> 'initial'"); + } + if (!showTranslated) { + queryBuilder.append(" AND state <> 'translated'"); + } + if (!showConfirmed) { + queryBuilder.append(" AND state <> 'final'"); + } + } + if (sortOption.equals("none")) { + queryBuilder.append(" ORDER BY file, child "); + } + if (sortOption.equals("source")) { + queryBuilder.append(" ORDER BY sourceText"); + } + if (sortOption.equals("target")) { + queryBuilder.append(" ORDER BY targetText"); + } + if (sortOption.equals("status")) { + queryBuilder.append(" ORDER BY state"); + } + if (sortDesc) { + queryBuilder.append(" DESC "); + } + queryBuilder.append(" LIMIT "); + queryBuilder.append(count); + queryBuilder.append(" OFFSET "); + queryBuilder.append(start); + try (ResultSet rs = stmt.executeQuery(queryBuilder.toString())) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segId = rs.getString(3); + String src = rs.getString(5); + String tgt = rs.getString(6); + int tags = rs.getInt(7); + String segState = rs.getString(8); + boolean segPreserve = "Y".equals(rs.getString(9)); + boolean segTranslate = "Y".equals(rs.getString(10)); + String sourceText = rs.getString(11); + String targetText = rs.getString(12); + int idx = rs.getInt(13); + + JSONObject tagsData = new JSONObject(); + if (tags > 0) { + tagsData = getUnitData(file, unit); + } + Element source = XliffUtils.buildElement(src); + + Element target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + if (tgt != null && !tgt.isBlank()) { + target = XliffUtils.buildElement(tgt); + } + + boolean checkErrors = segTranslate + && (segState.equals("final") || (segState.equals("translated") && acceptUnconfirmed)); + + boolean tagErrors = false; + boolean spaceErrors = false; + if (checkErrors) { + tagErrors = hasTagErrors(source, target); + spaceErrors = hasSpaceErrors(sourceText, targetText); + } + tagsMap = new Hashtable<>(); + JSONObject row = new JSONObject(); + row.put("index", idx); + row.put("file", file); + row.put("unit", unit); + row.put("segment", segId); + row.put("state", segState); + row.put("translate", segTranslate); + row.put("preserve", segPreserve); + tag = 1; + row.put("source", addHtmlTags(source, filterText, caseSensitiveFilter, regExp, tagsData, segPreserve)); + tag = 1; + row.put("target", addHtmlTags(target, filterText, caseSensitiveFilter, regExp, tagsData, segPreserve)); + row.put("match", getBestMatch(file, unit, segId)); + row.put("hasNotes", hasNotes(file, unit, segId)); + row.put("tagErrors", tagErrors); + row.put("spaceErrors", spaceErrors); + result.add(row); + } + } + return result; + } + + private boolean hasNotes(String file, String unit, String segId) throws SQLException { + boolean result = false; + getNotesStmt.setString(1, file); + getNotesStmt.setString(2, unit); + getNotesStmt.setString(3, segId); + try (ResultSet rs = getNotesStmt.executeQuery()) { + while (rs.next()) { + result = true; + } + } + return result; + } + + public JSONArray getNotes(String file, String unit, String segId) throws SQLException { + JSONArray array = new JSONArray(); + getNotesStmt.setString(1, file); + getNotesStmt.setString(2, unit); + getNotesStmt.setString(3, segId); + try (ResultSet rs = getNotesStmt.executeQuery()) { + while (rs.next()) { + JSONObject note = new JSONObject(); + note.put("id", rs.getString(1)); + note.put("note", rs.getString(2)); + array.put(note); + } + } + return array; + } + + public JSONArray addNote(String file, String unit, String segId, String noteText) throws SQLException { + String sql = "SELECT noteId FROM notes WHERE file=? AND unitId=? AND segId=?"; + int maxId = 0; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segId); + try (ResultSet rs = prep.executeQuery()) { + while (rs.next()) { + String id = rs.getString(1); + try { + int number = Integer.parseInt(id); + if (number > maxId) { + maxId = number; + } + } catch (NumberFormatException e) { + // ignore + } + } + } + } + sql = "INSERT INTO notes (file, unitId, segId, noteId, note) values (?,?,?,?,?)"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segId); + prep.setString(4, "" + (maxId + 1)); + prep.setString(5, noteText); + prep.executeUpdate(); + } + conn.commit(); + JSONArray array = new JSONArray(); + getNotesStmt.setString(1, file); + getNotesStmt.setString(2, unit); + getNotesStmt.setString(3, segId); + try (ResultSet rs = getNotesStmt.executeQuery()) { + while (rs.next()) { + JSONObject note = new JSONObject(); + note.put("id", rs.getString(1)); + note.put("note", rs.getString(2)); + array.put(note); + } + } + return array; + } + + public JSONArray removeNote(String file, String unit, String segId, String noteId) throws SQLException { + String sql = "DELETE FROM notes WHERE file=? AND unitId=? AND segId=? AND noteId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segId); + prep.setString(4, noteId); + prep.executeUpdate(); + } + conn.commit(); + JSONArray array = new JSONArray(); + getNotesStmt.setString(1, file); + getNotesStmt.setString(2, unit); + getNotesStmt.setString(3, segId); + try (ResultSet rs = getNotesStmt.executeQuery()) { + while (rs.next()) { + JSONObject note = new JSONObject(); + note.put("id", rs.getString(1)); + note.put("note", rs.getString(2)); + array.put(note); + } + } + return array; + } + + private boolean hasTagErrors(Element source, Element target) { + List sourceTags = tagsList(source); + List targetTags = tagsList(target); + if (sourceTags.size() != targetTags.size()) { + return true; + } + for (int i = 0; i < sourceTags.size(); i++) { + if (!sourceTags.get(i).equals(targetTags.get(i))) { + return true; + } + } + return false; + } + + private boolean hasSpaceErrors(String sourceText, String targetText) { + int[] sourceSpaces = countSpaces(sourceText); + int[] targetSpaces = countSpaces(targetText); + return sourceSpaces[0] != targetSpaces[0] || sourceSpaces[1] != targetSpaces[1]; + } + + private synchronized int getBestMatch(String file, String unit, String segment) throws SQLException { + String type = ""; + int similarity = 0; + bestMatch.setString(1, file); + bestMatch.setString(2, unit); + bestMatch.setString(3, segment); + try (ResultSet rs = bestMatch.executeQuery()) { + while (rs.next()) { + type = rs.getString(1); + similarity = rs.getInt(2); + } + } + if (type.isEmpty() || Constants.MT.equals(type) || Constants.AM.equals(type)) { + return 0; + } + return similarity; + } + + private synchronized JSONObject getUnitData(String file, String unit) throws SQLException, DataFormatException { + getUnitData.setString(1, file); + getUnitData.setString(2, unit); + String data = ""; + boolean compressed = false; + try (ResultSet rs = getUnitData.executeQuery()) { + while (rs.next()) { + data = rs.getString(1); + compressed = "Y".equals(rs.getString(2)); + } + } + if (data.isEmpty()) { + return new JSONObject(); + } + if (compressed) { + return new JSONObject(Compression.decompress(data)); + } + return new JSONObject(data); + } + + public void close() throws SQLException { + getUnitData.close(); + getSource.close(); + getTargetStmt.close(); + updateTargetStmt.close(); + insertMatch.close(); + updateMatch.close(); + getMatches.close(); + bestMatch.close(); + insertTerm.close(); + getTerms.close(); + checkTerm.close(); + getNotesStmt.close(); + getSegment.close(); + stmt.close(); + conn.commit(); + conn.close(); + if (TmsServer.isDebug()) { + logger.log(Level.INFO, Messages.getString("XliffStore.2")); + } + } + + public String getSrcLang() { + return srcLang; + } + + public String getTgtLang() { + return tgtLang; + } + + private static void getPreferences() throws IOException { + JSONObject json = TmsServer.getPreferences(); + acceptUnconfirmed = json.getBoolean("acceptUnconfirmed"); + caseSensitiveTermSearches = json.getBoolean("caseSensitiveSearches"); + caseSensitiveMatches = true; + if (json.has("caseSensitiveMatches")) { + caseSensitiveMatches = json.getBoolean("caseSensitiveMatches"); + } + fuzzyTermSearches = json.getBoolean("fuzzyTermSearches"); + catalog = json.getString("catalog"); + } + + public synchronized JSONObject saveSegment(JSONObject json) + throws IOException, SQLException, SAXException, ParserConfigurationException, DataFormatException { + + JSONObject result = new JSONObject(); + + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + String translation = json.getString("translation").replace(" ", "\u00A0").replace("
", "\n"); + boolean confirm = json.getBoolean("confirm"); + String memory = json.getString("memory"); + + String src = ""; + String pureSource = ""; + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + boolean wasFinal = false; + boolean translatable = true; + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + src = rs.getString(1); + pureSource = rs.getString(2); + wasFinal = rs.getString(3).equals("final"); + translatable = rs.getString(4).equals("Y"); + } + } + Element source = XliffUtils.buildElement(src); + + Map tags = getTags(source); + + translation = XliffUtils.clearHTML(translation); + + List list = XliffUtils.harvestTags(translation); + if (!list.isEmpty()) { + for (int i = 0; i < list.size(); i++) { + String code = list.get(i)[0]; + String img = list.get(i)[1]; + if (tags.containsKey(code)) { + translation = replace(translation, img, tags.get(code)); + } else { + translation = replace(translation, img, ""); + } + } + } + Element translated = XliffUtils.buildElement("" + translation + ""); + Element target = getTarget(file, unit, segment); + boolean unchanged = target.getContent().equals(translated.getContent()); + + target.setContent(translated.getContent()); + String pureTarget = XliffUtils.pureText(target); + JSONArray propagated = new JSONArray(); + updateTarget(file, unit, segment, target, pureTarget, confirm); + if (confirm && !pureTarget.isBlank() && (!unchanged || !wasFinal)) { + propagated = propagate(source, target); + } + result.put("propagated", propagated); + + boolean checkErrors = translatable && (confirm || !pureTarget.isEmpty()); + + boolean tagErrors = false; + boolean spaceErrors = false; + if (checkErrors) { + tagErrors = hasTagErrors(source, target); + spaceErrors = hasSpaceErrors(pureSource, pureTarget); + } + + result.put("tagErrors", tagErrors); + result.put("spaceErrors", spaceErrors); + + if (!memory.equals(Constants.NONE) && !pureTarget.isBlank() && confirm) { + Thread.ofVirtual().start(() -> { + try { + StringBuilder key = new StringBuilder(); + key.append(xliffFile.hashCode()); + key.append('-'); + key.append(file); + key.append('-'); + key.append(unit); + key.append('-'); + key.append(segment); + MemoriesHandler.open(memory); + ITmEngine engine = MemoriesHandler.getEngine(memory); + engine.storeTu(XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang)); + engine.commit(); + MemoriesHandler.close(memory); + } catch (IOException | SQLException | URISyntaxException e) { + logger.log(Level.ERROR, e); + } + }); + } + return result; + } + + public synchronized void saveSource(JSONObject json) + throws IOException, SQLException, SAXException, ParserConfigurationException { + + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + String newSource = json.getString("newSource").replace(" ", "\u00A0").replace("
", "\n"); + + String src = ""; + String pureSource = ""; + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + src = rs.getString(1); + pureSource = rs.getString(2); + } + } + Element source = XliffUtils.buildElement(src); + + Map tags = getTags(source); + + newSource = XliffUtils.clearHTML(newSource); + + List list = XliffUtils.harvestTags(newSource); + if (!list.isEmpty()) { + for (int i = 0; i < list.size(); i++) { + String code = list.get(i)[0]; + String img = list.get(i)[1]; + if (tags.containsKey(code)) { + newSource = replace(newSource, img, tags.get(code)); + } else { + newSource = replace(newSource, img, ""); + } + } + } + Element updated = XliffUtils.buildElement("" + newSource + ""); + if (source.getContent().equals(updated.getContent())) { + return; + } + + source.setContent(updated.getContent()); + pureSource = XliffUtils.pureText(source); + + String sql = "UPDATE segments SET source=?, sourceText=? WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, source.toString()); + prep.setString(2, pureSource); + prep.setString(3, file); + prep.setString(4, unit); + prep.setString(5, segment); + prep.executeUpdate(); + } + } + + public synchronized JSONObject getTranslationStatus() throws SQLException { + JSONObject result = new JSONObject(); + int total = 0; + int translated = 0; + int confirmed = 0; + int segments = 0; + String sql = "SELECT SUM(words), COUNT(*) FROM segments WHERE type='S'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + total = rs.getInt(1); + segments = rs.getInt(2); + } + } + sql = "SELECT SUM(words) FROM segments WHERE state='final' AND type='S'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + confirmed = rs.getInt(1); + } + } + sql = "SELECT SUM(words) FROM segments WHERE state <> 'initial' AND type='S'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + translated = rs.getInt(1); + } + } + int percentage = 0; + if (total != 0) { + percentage = Math.round(confirmed * 100f / total); + } + + result.put("segments", segments); + result.put("total", total); + result.put("translated", translated); + result.put("confirmed", confirmed); + result.put("percentage", percentage); + + MessageFormat mf = new MessageFormat( + Messages.getString("XliffStore.3")); + result.put("text", mf.format(new String[] { "" + segments, "" + total, "" + translated, "" + confirmed })); + result.put("svg", XliffUtils.makeSVG(percentage)); + return result; + } + + private synchronized void updateTarget(String file, String unit, String segment, Element target, String pureTarget, + boolean confirm) throws SQLException { + String segState = pureTarget.isBlank() ? Constants.INITIAL : Constants.TRANSLATED; + if (confirm) { + segState = Constants.FINAL; + } + updateTargetStmt.setString(1, target.toString()); + updateTargetStmt.setString(2, pureTarget); + updateTargetStmt.setString(3, segState); + updateTargetStmt.setString(4, file); + updateTargetStmt.setString(5, unit); + updateTargetStmt.setString(6, segment); + updateTargetStmt.executeUpdate(); + conn.commit(); + } + + private JSONArray propagate(Element source, Element target) + throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { + JSONArray result = new JSONArray(); + String dummySource = dummyTagger(source); + String query = "SELECT file, unitId, segId, source, state, tags, space FROM segments WHERE translate='Y' AND type='S' AND state <> 'final' "; + try (ResultSet rs = stmt.executeQuery(query)) { + while (rs.next()) { + Element candidate = XliffUtils.buildElement(rs.getString(4)); + int differences = tagDifferences(source, candidate); + String dummy = dummyTagger(candidate); + int similarity = MatchQuality.similarity(dummySource, dummy) - differences; + if (similarity > THRESHOLD) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + Element sourceElement = XliffUtils.buildElement(rs.getString(4)); + int tags = rs.getInt(6); + JSONObject tagsData = new JSONObject(); + if (tags > 0) { + tagsData = getUnitData(file, unit); + } + if (similarity == 100 && Constants.INITIAL.equals(state)) { + tagsMap = new Hashtable<>(); + tag = 1; + addHtmlTags(candidate, "", false, false, tagsData, true); + + JSONObject row = new JSONObject(); + row.put("file", file); + row.put("unit", unit); + row.put("segment", segment); + row.put("match", 100); + tag = 1; + String translation = addHtmlTags(target, "", false, false, tagsData, true); + row.put("target", translation); + result.put(row); + + Element translated = XliffUtils.buildElement("" + translation + ""); + translated.setAttribute("xml:space", preserve ? "preserve" : "default"); + translated.setContent(target.getContent()); + if (!translated.getChildren().isEmpty()) { + translated = fixTags(sourceElement, source, target); + } + updateTarget(file, unit, segment, translated, XliffUtils.pureText(translated), false); + } + insertMatch(file, unit, segment, "Self", Constants.TM, similarity, source, target, tagsData); + conn.commit(); + int best = getBestMatch(file, unit, segment); + JSONObject row = new JSONObject(); + row.put("file", file); + row.put("unit", unit); + row.put("segment", segment); + row.put("match", best); + result.put(row); + } + } + } + return result; + } + + private int tagDifferences(Element source, Element candidate) { + int a = source.getChildren().size(); + int b = candidate.getChildren().size(); + if (a > b) { + return a - b; + } + return b - a; + } + + private synchronized void insertMatch(String file, String unit, String segment, String origin, String type, + int similarity, Element source, Element target, JSONObject tagsData) throws SQLException { + String matchId = "" + XliffUtils.pureText(source).hashCode() * origin.hashCode(); + if (Constants.MT.equals(type)) { + matchId = origin; + } + JSONArray matches = getMatches(file, unit, segment); + String data = ""; + if (!source.getChildren().isEmpty() || !target.getChildren().isEmpty()) { + List added = new Vector<>(); + Element originalData = new Element("originalData"); + List children = source.getChildren(); + Iterator it = children.iterator(); + while (it.hasNext()) { + Element e = it.next(); + if ("mrk".equals(e.getName()) || "pc".equals(e.getName()) || "cp".equals(e.getName())) { + continue; + } + String dataRef = e.getAttributeValue("dataRef"); + if (!added.contains(dataRef) && tagsData.has(dataRef)) { + Element d = new Element("data"); + d.setAttribute("id", dataRef); + d.setText(tagsData.getString(dataRef)); + originalData.addContent(d); + added.add(dataRef); + continue; + } + e.removeAttribute("dataRef"); + } + children = target.getChildren(); + it = children.iterator(); + while (it.hasNext()) { + Element e = it.next(); + if ("mrk".equals(e.getName()) || "pc".equals(e.getName()) || "cp".equals(e.getName())) { + continue; + } + String dataRef = e.getAttributeValue("dataRef"); + if (added.contains(dataRef)) { + continue; + } + if (!added.contains(dataRef) && tagsData.has(dataRef)) { + Element d = new Element("data"); + d.setAttribute("id", dataRef); + d.setText(tagsData.getString(dataRef)); + originalData.addContent(d); + added.add(dataRef); + continue; + } + e.removeAttribute("dataRef"); + } + data = originalData.toString(); + } + for (int i = 0; i < matches.length(); i++) { + JSONObject match = matches.getJSONObject(i); + if (match.getString("matchId").equals(matchId)) { + updateMatch.setString(1, origin); + updateMatch.setString(2, type); + updateMatch.setInt(3, similarity); + updateMatch.setString(4, source.toString()); + updateMatch.setString(5, target.toString()); + updateMatch.setString(6, data); + updateMatch.setString(7, "N"); + updateMatch.setString(8, file); + updateMatch.setString(9, unit); + updateMatch.setString(10, segment); + updateMatch.setString(11, matchId); + updateMatch.execute(); + return; + } + } + insertMatch.setString(1, file); + insertMatch.setString(2, unit); + insertMatch.setString(3, segment); + insertMatch.setString(4, matchId); + insertMatch.setString(5, origin); + insertMatch.setString(6, type); + insertMatch.setInt(7, similarity); + insertMatch.setString(8, source.toString()); + insertMatch.setString(9, target.toString()); + insertMatch.setString(10, data); + insertMatch.setString(11, "N"); + insertMatch.execute(); + } + + private JSONArray getMatches(String file, String unit, String segment) throws SQLException { + JSONArray result = new JSONArray(); + getMatches.setString(1, file); + getMatches.setString(2, unit); + getMatches.setString(3, segment); + try (ResultSet rs = getMatches.executeQuery()) { + while (rs.next()) { + JSONObject match = new JSONObject(); + match.put("file", file); + match.put("unit", unit); + match.put("segment", segment); + match.put("matchId", rs.getString(4)); + match.put("origin", rs.getString(5)); + match.put("type", rs.getString(6)); + match.put("similarity", rs.getInt(7)); + match.put("source", rs.getString(8)); + match.put("srcLang", srcLang); + match.put("target", rs.getString(9)); + match.put("tgtLang", tgtLang); + result.put(match); + } + } + return result; + } + + private String dummyTagger(Element e) { + if (e == null) { + return ""; + } + int dummy = 1; + StringBuilder string = new StringBuilder(); + List content = e.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode n = it.next(); + if (n.getNodeType() == XMLNode.TEXT_NODE) { + TextNode t = (TextNode) n; + string.append(t.getText()); + } + if (n.getNodeType() == XMLNode.ELEMENT_NODE) { + Element el = (Element) n; + if ("mrk".equals(el.getName()) || "pc".equals(el.getName())) { + string.append((char) (0xF300 + dummy++)); + string.append(dummyTagger(el)); + string.append((char) (0xF300 + dummy++)); + } else { + string.append((char) (0xF300 + dummy++)); + } + } + } + return string.toString(); + } + + private String addHtmlTags(Element seg, JSONObject originalData) throws IOException { + if (seg == null) { + return ""; + } + List list = seg.getContent(); + Iterator it = list.iterator(); + StringBuilder text = new StringBuilder(); + while (it.hasNext()) { + XMLNode o = it.next(); + if (o.getNodeType() == XMLNode.TEXT_NODE) { + text.append(XliffUtils.cleanString(((TextNode) o).getText())); + } else if (o.getNodeType() == XMLNode.ELEMENT_NODE) { + // paired: , , + Element e = (Element) o; + String type = e.getName(); + if (type.equals("pc")) { + String id = e.getAttributeValue("id"); + if (!tagsMap.containsKey("pc" + id)) { + XliffUtils.checkSVG(tag); + String header = XliffUtils.getHeader(e); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("pc" + id, sb.toString()); + } + text.append(tagsMap.get("pc" + id)); + text.append(addHtmlTags(e, originalData)); + if (!tagsMap.containsKey("/pc" + id)) { + XliffUtils.checkSVG(tag); + String tail = ""; + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("/pc" + id, sb.toString()); + } + text.append("/" + tagsMap.get(e.getName() + id)); + } else if (type.equals("mrk")) { + String id = e.getAttributeValue("id"); + if (!tagsMap.containsKey("mrk" + id)) { + XliffUtils.checkSVG(tag); + String header = XliffUtils.getHeader(e); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("mrk" + id, sb.toString()); + } + text.append(tagsMap.get(e.getName() + id)); + text.append(""); + text.append(e.getText()); + text.append(""); + if (!tagsMap.containsKey("/mrk" + id)) { + XliffUtils.checkSVG(tag); + String tail = ""; + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("/mrk" + id, sb.toString()); + } + text.append(tagsMap.get("/mrk" + id)); + } else if (type.equals("cp")) { + // empty - special case + String hex = "cp" + e.getAttributeValue("hex"); + if (!tagsMap.containsKey(hex)) { + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put(hex, sb.toString()); + } + text.append(tagsMap.get(hex)); + } else { + // empty: , , , and . + String key = e.getName() + e.getAttributeValue("id"); + if (!tagsMap.containsKey(key)) { + String dataRef = e.getAttributeValue("dataRef"); + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put(key, sb.toString()); + } + text.append(tagsMap.get(key)); + } + } + } + return text.toString(); + } + + private String addHtmlTags(Element seg, String filterText, boolean caseSensitive, boolean regExp, + JSONObject originalData, boolean preserve) throws IOException { + if (seg == null) { + return ""; + } + List list = seg.getContent(); + Iterator it = list.iterator(); + StringBuilder text = new StringBuilder(); + while (it.hasNext()) { + XMLNode o = it.next(); + if (o.getNodeType() == XMLNode.TEXT_NODE) { + if (filterText == null || filterText.isEmpty()) { + text.append(XliffUtils.cleanString(((TextNode) o).getText())); + } else { + if (regExp) { + if (pattern == null || !filterText.equals(lastFilterText)) { + pattern = Pattern.compile(filterText); + lastFilterText = filterText; + } + String s = ((TextNode) o).getText(); + Matcher matcher = pattern.matcher(s); + if (matcher.find()) { + StringBuilder sb = new StringBuilder(); + do { + int start = matcher.start(); + int end = matcher.end(); + sb.append(XliffUtils.cleanString(s.substring(0, start))); + sb.append(""); + sb.append(XliffUtils.cleanString(s.substring(start, end))); + sb.append(""); + s = s.substring(end); + matcher = pattern.matcher(s); + } while (matcher.find()); + sb.append(XliffUtils.cleanString(s)); + text.append(sb.toString()); + } else { + text.append(XliffUtils.cleanString(s)); + } + } else { + String s = XliffUtils.cleanString(((TextNode) o).getText()); + String t = XliffUtils.cleanString(filterText); + if (caseSensitive) { + if (s.indexOf(t) != -1) { + text.append(XliffUtils.highlight(s, t, caseSensitive)); + } else { + text.append(s); + } + } else { + if (s.toLowerCase().indexOf(t.toLowerCase()) != -1) { + text.append(XliffUtils.highlight(s, t, caseSensitive)); + } else { + text.append(s); + } + } + } + } + } else if (o.getNodeType() == XMLNode.ELEMENT_NODE) { + text.append(inline2html((Element) o, originalData)); + } + } + return preserve ? XliffUtils.highlightSpaces(text.toString()) : text.toString().trim(); + } + + private String inline2html(Element e, JSONObject originalData) throws IOException { + // empty: , , , , and . + // paired: , , + StringBuilder text = new StringBuilder(); + String type = e.getName(); + if (type.equals("pc")) { + String id = e.getAttributeValue("id"); + if (!tagsMap.containsKey("pc" + id)) { + XliffUtils.checkSVG(tag); + String header = XliffUtils.getHeader(e); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("pc" + id, sb.toString()); + } + text.append(tagsMap.get("pc" + id)); + List content = e.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.TEXT_NODE) { + String s = ((TextNode) node).getText(); + text.append(XMLUtils.cleanText(s)); + } + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + text.append(inline2html((Element) node, originalData)); + } + } + if (!tagsMap.containsKey("/pc" + id)) { + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""))); + sb.append("\"/>"); + tagsMap.put("/pc" + id, sb.toString()); + } + text.append(tagsMap.get("/pc" + id)); + } else if (type.equals("mrk")) { + String id = e.getAttributeValue("id"); + boolean isTerm = e.getAttributeValue("type").equals("term"); + if (!isTerm) { + if (!tagsMap.containsKey("mrk" + id)) { + XliffUtils.checkSVG(tag); + String header = XliffUtils.getHeader(e); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("mrk" + id, sb.toString()); + } + text.append(tagsMap.get(e.getName() + id)); + text.append(""); + } else { + text.append(""); + } + List content = e.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.TEXT_NODE) { + String s = ((TextNode) node).getText(); + text.append(XMLUtils.cleanText(s)); + } + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + text.append(inline2html((Element) node, originalData)); + } + } + text.append(""); + if (!isTerm) { + if (!tagsMap.containsKey("/mrk" + id)) { + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""))); + sb.append("\"/>"); + tagsMap.put("/mrk" + id, sb.toString()); + } + text.append(tagsMap.get("/mrk" + id)); + } + } else if (type.equals("cp")) { + String hex = "cp" + e.getAttributeValue("hex"); + if (!tagsMap.containsKey(hex)) { + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put(hex, sb.toString()); + } + text.append(tagsMap.get(hex)); + } else if ("ph".equals(type)) { + String id = e.getAttributeValue("id"); + if (!tagsMap.containsKey("ph" + id)) { + XliffUtils.checkSVG(tag); + String title = originalData.has(id) ? originalData.getString(id) : e.toString(); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put("ph" + id, sb.toString()); + } + text.append(tagsMap.get("ph" + id)); + } else { + String dataRef = e.getAttributeValue("dataRef"); + if (!tagsMap.containsKey(dataRef)) { + XliffUtils.checkSVG(tag); + StringBuilder sb = new StringBuilder(); + sb.append(""); + tagsMap.put(dataRef, sb.toString()); + } + text.append(tagsMap.get(dataRef)); + } + return text.toString(); + } + + private String replace(String source, String target, String replacement) { + int start = source.indexOf(target); + while (start != -1) { + source = source.substring(0, start) + replacement + source.substring(start + target.length()); + start += replacement.length(); + start = source.indexOf(target, start); + } + return source; + } + + private Map getTags(Element root) { + Map result = new Hashtable<>(); + List content = root.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("mrk".equals(e.getName()) || "pc".equals(e.getName())) { + result.put(e.getAttributeValue("id"), XliffUtils.getHeader(e)); + result.put("/" + e.getAttributeValue("id"), XliffUtils.getTail(e)); + Map map = getTags(e); + result.putAll(map); + } else if ("cp".equals(e.getName())) { + result.put("cp" + e.getAttributeValue("hex"), e.toString()); + } else { + result.put(e.getAttributeValue("id"), e.toString()); + } + } + } + return result; + } + + public synchronized JSONArray getTaggedtMatches(JSONObject json) + throws SQLException, SAXException, IOException, ParserConfigurationException, DataFormatException { + JSONArray result = new JSONArray(); + + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + + JSONObject originalData = getUnitData(file, unit); + Element originalSource = null; + + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + String src = rs.getString(1); + originalSource = XliffUtils.buildElement(src); + } + } + List originalTags = originalSource.getChildren(); + String dummySource = dummyTagger(originalSource); + + getMatches.setString(1, file); + getMatches.setString(2, unit); + getMatches.setString(3, segment); + try (ResultSet rs = getMatches.executeQuery()) { + while (rs.next()) { + tag = 1; + JSONObject match = new JSONObject(); + match.put("file", file); + match.put("unit", unit); + match.put("segment", segment); + match.put("matchId", rs.getString(4)); + match.put("origin", rs.getString(5)); + match.put("type", rs.getString(6)); + match.put("similarity", rs.getInt(7)); + match.put("srcLang", srcLang); + match.put("tgtLang", tgtLang); + + String src = rs.getString(8); + Element source = XliffUtils.buildElement(src); + String tgt = rs.getString(9); + Element target = XliffUtils.buildElement(tgt); + + List sourceTags = source.getChildren(); + List targetTags = target.getChildren(); + + for (int i = 0; i < sourceTags.size(); i++) { + Element sourceTag = sourceTags.get(i); + for (int j = 0; j < targetTags.size(); j++) { + Element targetTag = targetTags.get(j); + if (sourceTag.equals(targetTag) && i < originalTags.size()) { + targetTag.clone(originalTags.get(i)); + } + } + if (i < originalTags.size()) { + sourceTag.clone(originalTags.get(i)); + } + } + + tagsMap = new Hashtable<>(); + String taggedSource = addHtmlTags(source, originalData); + + List tags = XliffUtils.harvestTags(taggedSource); + for (int i = 0; i < tags.size(); i++) { + taggedSource = taggedSource.replace(tags.get(i)[1], "" + (char) (0xF300 + (i + 1))); + } + + DifferenceTagger tagger = new DifferenceTagger(dummySource, taggedSource); + String tagged = tagger.getYDifferences(); + for (int i = 0; i < tags.size(); i++) { + tagged = tagged.replace("" + (char) (0xF300 + (i + 1)), tags.get(i)[1]); + } + + match.put("source", tagged); + match.put("target", addHtmlTags(target, originalData)); + result.put(match); + } + } + return result; + } + + public void exportXliff(String output) + throws SAXException, IOException, ParserConfigurationException, SQLException { + updateXliff(); + File projectFolder = new File(xliffFile).getParentFile(); + File tempFolder = new File(projectFolder, "tmp"); + if (tempFolder.exists()) { + TmsServer.deleteFolder(tempFolder); + } + List files = Split.split(xliffFile, tempFolder.getAbsolutePath()); + File tempFile = File.createTempFile("joined", ".xlf", tempFolder); + Join.join(files, tempFile.getAbsolutePath()); + File outputFile = new File(output); + if (outputFile.exists()) { + Files.delete(outputFile.toPath()); + } + Files.copy(tempFile.toPath(), outputFile.toPath()); + Files.delete(tempFile.toPath()); + TmsServer.deleteFolder(tempFolder); + } + + public void exportTMX(String output, String description, String client, String subject) + throws SQLException, SAXException, IOException, ParserConfigurationException { + Element d = new Element("prop"); + d.setAttribute("type", "project"); + d.setText(description); + Element c = null; + if (!client.isBlank()) { + c = new Element("prop"); + c.setAttribute("type", "customer"); + c.setText(client); + } + Element s = null; + if (!subject.isBlank()) { + s = new Element("prop"); + s.setAttribute("type", "subject"); + s.setText(subject); + } + try (FileOutputStream out = new FileOutputStream(output)) { + writeTmxHeader(out); + String sql = "SELECT file, unitId, segId, source, target FROM segments WHERE type='S' AND state='final' ORDER BY SELECT file, unitId, segId"; + + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + + StringBuilder key = new StringBuilder(); + key.append(xliffFile.hashCode()); + key.append('-'); + key.append(file); + key.append('-'); + key.append(unit); + key.append('-'); + key.append(segment); + + String src = rs.getString(4); + Element source = XliffUtils.buildElement(src); + String tgt = rs.getString(5); + Element target = XliffUtils.buildElement(tgt); + + Map tags = getTags(source); + + Element tuv = XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang); + tuv.getContent().add(0, d); + if (c != null) { + tuv.getContent().add(0, c); + } + if (s != null) { + tuv.getContent().add(0, s); + } + Indenter.indent(tuv, 2); + writeString(out, tuv.toString()); + } + } + writeString(out, "\n"); + writeString(out, "\n"); + } + } + + private void writeTmxHeader(FileOutputStream out) throws IOException { + writeString(out, "\n"); + writeString(out, + "\n"); + writeString(out, "\n"); + writeString(out, + "
\n"); + writeString(out, "\n"); + } + + public void exportTranslations(String output) + throws SAXException, IOException, ParserConfigurationException, SQLException { + updateXliff(); + getPreferences(); + File adjusted = reviewStates(); + List result = Merge.merge(adjusted.getAbsolutePath(), output, catalog, acceptUnconfirmed); + if (!"0".equals(result.get(0))) { + throw new IOException(result.get(1)); + } + Files.delete(adjusted.toPath()); + } + + private File reviewStates() throws SAXException, IOException, ParserConfigurationException { + File xliff = new File(xliffFile); + File adjusted = new File(xliff.getParentFile(), "adjusted.xlf"); + document = builder.build(xliffFile); + recurseStates(document.getRootElement()); + XMLOutputter outputter = new XMLOutputter(); + outputter.preserveSpace(true); + Indenter.indent(document.getRootElement(), 2); + try (FileOutputStream out = new FileOutputStream(adjusted)) { + outputter.output(document, out); + } + return adjusted; + } + + private void recurseStates(Element e) { + if ("segment".equals(e.getName())) { + if ("initial".equals(e.getAttributeValue("state"))) { + Element source = e.getChild("source"); + Element target = e.getChild("target"); + if (target == null) { + target = new Element("target"); + if ("preserve".equals(source.getAttributeValue("xml:space", "default"))) { + target.setAttribute("xml:space", "preserve"); + } + e.addContent(target); + } + target.setContent(source.getContent()); + } + return; + } + if ("ignorable".equals(e.getName())) { + Element target = e.getChild("target"); + if (target == null) { + Element source = e.getChild("source"); + target = new Element("target"); + if ("preserve".equals(source.getAttributeValue("xml:space", "default"))) { + target.setAttribute("xml:space", "preserve"); + } + target.setContent(source.getContent()); + e.addContent(target); + } + return; + } + List children = e.getChildren(); + Iterator it = children.iterator(); + while (it.hasNext()) { + recurseStates(it.next()); + } + } + + public void updateXliff() throws SQLException, SAXException, IOException, ParserConfigurationException { + document = builder.build(xliffFile); + document.getRootElement().setAttribute("xmlns:mtc", "urn:oasis:names:tc:xliff:matches:2.0"); + document.getRootElement().setAttribute("xmlns:gls", "urn:oasis:names:tc:xliff:glossary:2.0"); + unitMatches = conn.prepareStatement( + "SELECT file, unitId, segId, matchId, origin, type, similarity, source, target, data, compressed FROM matches WHERE file=? AND unitId=? ORDER BY segId, similarity DESC"); + unitTerms = conn.prepareStatement( + "SELECT file, unitId, segId, termId, origin, source, target FROM terms WHERE file=? AND unitId=? ORDER BY segId"); + unitNotes = conn + .prepareStatement("SELECT segId, noteId, note FROM notes WHERE file=? AND unitId=? ORDER BY segId"); + recurseUpdating(document.getRootElement()); + unitTerms.close(); + unitMatches.close(); + unitNotes.close(); + saveXliff(); + } + + private void recurseUpdating(Element e) + throws SQLException, SAXException, IOException, ParserConfigurationException { + if ("file".equals(e.getName())) { + currentFile = e.getAttributeValue("id"); + index = 0; + } + if ("unit".equals(e.getName())) { + tagCount = 0; + currentUnit = e.getAttributeValue("id"); + Element glossary = getUnitTerms(currentFile, currentUnit); + if (glossary != null) { + insertGlossary(e, glossary); + } + Element matches = getUnitMatches(currentFile, currentUnit); + Map matchesData = new HashMap<>(); + if (matches != null) { + List matchesList = matches.getChildren("mtc:match"); + Iterator it = matchesList.iterator(); + while (it.hasNext()) { + Element match = it.next(); + Element originalData = match.getChild("originalData"); + if (originalData != null) { + List dataList = originalData.getChildren("data"); + for (int i = 0; i < dataList.size(); i++) { + Element data = dataList.get(i); + matchesData.put(data.getAttributeValue("id"), data); + } + } + } + insertMatches(e, matches); + } + if (!matchesData.isEmpty()) { + Element originalData = e.getChild("originalData"); + if (originalData == null) { + originalData = new Element("originalData"); + insertOriginalData(e, originalData); + } + Map oldData = new HashMap<>(); + List dataList = originalData.getChildren("data"); + for (int i = 0; i < dataList.size(); i++) { + Element data = dataList.get(i); + oldData.put(data.getAttributeValue("id"), data); + } + Set keys = matchesData.keySet(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + String key = it.next(); + if (!oldData.containsKey(key)) { + oldData.put(key, matchesData.get(key)); + originalData.addContent(matchesData.get(key)); + } + } + } + Element notes = getUnitNotes(currentFile, currentUnit); + if (notes != null) { + insertNotes(e, notes); + } + } + if ("segment".equals(e.getName())) { + String id = e.getAttributeValue("id"); + Element source = e.getChild("source"); + Element target = e.getChild("target"); + if (target == null) { + target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + e.addContent(target); + } + Element updated = getTarget(currentFile, currentUnit, id); + target.setContent(updated.getContent()); + String st = getState(currentFile, currentUnit, id); + boolean translate = isTranslatable(currentFile, currentUnit, id); + if (Constants.INITIAL.equals(st) && !target.getContent().isEmpty()) { + st = Constants.TRANSLATED; + logger.log(Level.WARNING, Messages.getString("XliffStore.4")); + } + JSONArray notesArray = getNotes(currentFile, currentUnit, id); + if (notesArray.length() > 0) { + target = FromXliff2.removeComments(target); + for (int i = 0; i < notesArray.length(); i++) { + JSONObject json = notesArray.getJSONObject(i); + String noteId = json.getString("id"); + Element mrk = new Element("mrk"); + mrk.setAttribute("id", "tn" + noteId); + mrk.setAttribute("type", "comment"); + mrk.setAttribute("ref", "#n=" + noteId); + mrk.setContent(target.getContent()); + List content = new Vector<>(); + content.add(mrk); + target.setContent(content); + } + } + e.setAttribute("state", st); + if (translate) { + e.removeAttribute("subState"); + } else { + e.setAttribute("subState", "openxliff:locked"); + } + if (Constants.INITIAL.equals(st) && target.getContent().isEmpty()) { + e.removeChild(target); + } + } + if ("ignorable".equals(e.getName())) { + Element source = e.getChild("source"); + Element target = e.getChild("target"); + if (target == null) { + target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + e.addContent(target); + } + target.setContent(source.getContent()); + } + List children = e.getChildren(); + Iterator it = children.iterator(); + while (it.hasNext()) { + recurseUpdating(it.next()); + } + } + + private void insertOriginalData(Element unit, Element originalData) { + List newContent = new Vector<>(); + boolean added = false; + List oldContent = unit.getContent(); + Iterator it = oldContent.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("segment".equals(e.getName()) || "ignorable".equals(e.getName()) && !added) { + newContent.add(originalData); + added = true; + } + newContent.add(node); + } else { + newContent.add(node); + } + } + unit.setContent(newContent); + } + + private void insertMatches(Element unit, Element matches) { + Element old = unit.getChild("mtc:matches"); + if (old != null) { + unit.removeChild(old); + } + unit.getContent().add(0, matches); + } + + private void insertNotes(Element unit, Element notes) { + Element old = unit.getChild("notes"); + if (old != null) { + unit.removeChild(old); + } + boolean added = false; + List newContent = new Vector<>(); + List oldContent = unit.getContent(); + Iterator it = oldContent.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if (e.getNamespace().isEmpty() && !added) { + newContent.add(notes); + added = true; + } + newContent.add(node); + } else { + newContent.add(node); + } + } + unit.setContent(newContent); + } + + private void insertGlossary(Element unit, Element terms) { + Element old = unit.getChild("gls:glossary"); + if (old != null) { + unit.removeChild(old); + } + unit.getContent().add(0, terms); + } + + private Element getUnitMatches(String file, String unit) + throws SQLException, SAXException, IOException, ParserConfigurationException { + Element matches = new Element("mtc:matches"); + unitMatches.setString(1, file); + unitMatches.setString(2, unit); + try (ResultSet rs = unitMatches.executeQuery()) { + while (rs.next()) { + Element match = new Element("mtc:match"); + match.setAttribute("ref", "#" + rs.getString(3)); + match.setAttribute("origin", rs.getString(5)); + match.setAttribute("type", rs.getString(6)); + match.setAttribute("matchQuality", "" + rs.getInt(7)); + Element originalData = new Element("originalData"); + String data = rs.getString(10); + if (!data.isEmpty()) { + originalData = XliffUtils.buildElement(data); + List newData = new Vector<>(); + List oldData = originalData.getChildren(); + Iterator it = oldData.iterator(); + while (it.hasNext()) { + Element d = it.next(); + if (!d.getContent().isEmpty()) { + newData.add(d); + } + } + originalData.setChildren(newData); + if (!newData.isEmpty()) { + match.addContent(originalData); + } + } + Set dataRefs = new HashSet<>(); + Iterator it = originalData.getChildren().iterator(); + while (it.hasNext()) { + dataRefs.add(it.next().getAttributeValue("id")); + } + match.addContent(XliffUtils.buildElement(rs.getString(8))); + match.addContent(XliffUtils.buildElement(rs.getString(9))); + removeMissingReferences(match.getChild("source"), dataRefs); + removeMissingReferences(match.getChild("target"), dataRefs); + matches.addContent(match); + } + } + return matches.getChildren("mtc:match").isEmpty() ? null : matches; + } + + private void removeMissingReferences(Element child, Set references) { + List tags = child.getChildren(); + Iterator it = tags.iterator(); + while (it.hasNext()) { + Element e = it.next(); + String dataRef = e.getAttributeValue("dataRef"); + if (!dataRef.isEmpty() && !references.contains(dataRef)) { + e.removeAttribute("dataRef"); + } + } + } + + private Element getUnitNotes(String file, String unit) throws SQLException { + Element notes = new Element("notes"); + unitNotes.setString(1, file); + unitNotes.setString(2, unit); + try (ResultSet rs = unitNotes.executeQuery()) { + while (rs.next()) { + Element note = new Element("note"); + note.setAttribute("id", rs.getString(2)); + note.setText(rs.getString(3)); + notes.addContent(note); + } + } + return notes.getChildren().isEmpty() ? null : notes; + } + + private Element getUnitTerms(String file, String unit) throws SQLException { + Element glossary = new Element("gls:glossary"); + unitTerms.setString(1, file); + unitTerms.setString(2, unit); + try (ResultSet rs = unitTerms.executeQuery()) { + while (rs.next()) { + Element entry = new Element("gls:glossEntry"); + entry.setAttribute("ref", "#" + rs.getString(3)); + glossary.addContent(entry); + + Element term = new Element("gls:term"); + term.setAttribute("source", rs.getString(5)); + term.setText(rs.getString(6)); + entry.addContent(term); + + Element translation = new Element("gls:translation"); + translation.setText(rs.getString(7)); + entry.addContent(translation); + } + } + return glossary.getChildren().isEmpty() ? null : glossary; + } + + public void assembleMatches(JSONObject json) + throws SAXException, IOException, ParserConfigurationException, SQLException, URISyntaxException { + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + + String pure = ""; + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + pure = rs.getString(2); + } + } + + String memory = json.getString("memory"); + MemoriesHandler.open(memory); + ITmEngine tmEngine = MemoriesHandler.getEngine(memory); + List tmMatches = tmEngine.searchTranslation(pure, srcLang, tgtLang, 60, false); + MemoriesHandler.close(memory); + + String glossary = json.getString("glossary"); + GlossariesHandler.openGlossary(glossary); + ITmEngine glossEngine = GlossariesHandler.getEngine(glossary); + + Match match = MatchAssembler.assembleMatch(pure, tmMatches, glossEngine, srcLang, tgtLang); + if (match != null) { + Element matchSource = match.getSource(); + matchSource.setAttribute("xml:lang", srcLang); + Element matchTarget = match.getTarget(); + matchTarget.setAttribute("xml:lang", tgtLang); + insertMatch(file, unit, segment, "Auto", Constants.AM, match.getSimilarity(), matchSource, matchTarget, + new JSONObject()); + conn.commit(); + } + GlossariesHandler.closeGlossary(glossary); + } + + public void assembleMatchesAll(JSONObject json) + throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { + + String memory = json.getString("memory"); + MemoriesHandler.open(memory); + ITmEngine tmEngine = MemoriesHandler.getEngine(memory); + + String glossary = json.getString("glossary"); + GlossariesHandler.openGlossary(glossary); + ITmEngine glossEngine = GlossariesHandler.getEngine(glossary); + + String sql = "SELECT file, unitId, segId, sourceText FROM segments WHERE state <> 'final'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String pure = rs.getString(4); + try { + List tmMatches = tmEngine.searchTranslation(pure, srcLang, tgtLang, 60, false); + Match match = MatchAssembler.assembleMatch(pure, tmMatches, glossEngine, srcLang, tgtLang); + if (match != null) { + Element matchSource = match.getSource(); + matchSource.setAttribute("xml:lang", srcLang); + Element matchTarget = match.getTarget(); + matchTarget.setAttribute("xml:lang", tgtLang); + insertMatch(file, unit, segment, "Auto", Constants.AM, match.getSimilarity(), matchSource, + matchTarget, new JSONObject()); + conn.commit(); + } + } catch (IOException | ParserConfigurationException | SAXException | SQLException ex) { + // Ignore errors in individual segments + JSONObject errorSegment = new JSONObject(); + errorSegment.put("file", file); + errorSegment.put("unit", unit); + errorSegment.put("segment", segment); + MessageFormat mf = new MessageFormat(Messages.getString("XliffStore.5")); + logger.log(Level.WARNING, mf.format(new String[] { ex.getMessage(), errorSegment.toString() })); + } + } + } + MemoriesHandler.close(memory); + GlossariesHandler.closeGlossary(glossary); + } + + public JSONArray tmTranslate(JSONObject json) throws SAXException, IOException, ParserConfigurationException, + SQLException, DataFormatException, URISyntaxException { + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + String memory = json.getString("memory"); + + String src = ""; + String pure = ""; + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + src = rs.getString(1); + pure = rs.getString(2); + } + } + Element original = XliffUtils.buildElement(src); + String memoryName = MemoriesHandler.getName(memory); + MemoriesHandler.open(memory); + ITmEngine engine = MemoriesHandler.getEngine(memory); + List matches = engine.searchTranslation(pure, srcLang, tgtLang, 60, caseSensitiveMatches); + for (int i = 0; i < matches.size(); i++) { + Match m = matches.get(i); + XliffUtils.setTags(new JSONObject()); + Element matchSource = XliffUtils.toXliff(segment, i, "source", m.getSource()); + matchSource.setAttribute("xml:lang", srcLang); + Element matchTarget = XliffUtils.toXliff(segment, i, "target", m.getTarget()); + matchTarget.setAttribute("xml:lang", tgtLang); + JSONObject obj = new JSONObject(); + obj.put("dataRef", XliffUtils.getTags()); + int similarity = m.getSimilarity() - tagDifferences(original, matchSource); + insertMatch(file, unit, segment, memoryName, Constants.TM, similarity, matchSource, matchTarget, obj); + conn.commit(); + } + MemoriesHandler.close(memory); + return getTaggedtMatches(json); + } + + public int tmTranslateAll(String memory, int penalization, Map processes, String processId) + throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { + String memoryName = MemoriesHandler.getName(memory); + MemoriesHandler.open(memory); + ITmEngine engine = MemoriesHandler.getEngine(memory); + String sql = "SELECT COUNT(*) FROM segments WHERE type = 'S' AND state <> 'final'"; + int total = 0; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + total = rs.getInt(1); + } + } + if (total == 0) { + return 0; + } + int processed = 0; + int offset = 0; + do { + processed += translateBatch(offset, engine, memoryName, penalization); + int percentage = Math.round(offset * 100f / total); + if (percentage == 100) { + percentage = 99; + } + processes.get(processId).put("percentage", percentage); + offset += BATCHSIZE; + } while (offset < total); + MemoriesHandler.close(memory); + return processed; + } + + private synchronized int translateBatch(int offset, ITmEngine engine, String memoryName, int penalization) + throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { + StringBuilder sb = new StringBuilder(); + sb.append( + "SELECT file, unitId, segId, source, sourceText, target FROM segments WHERE type = 'S' AND state <> 'final' LIMIT "); + sb.append(BATCHSIZE); + sb.append(" OFFSET "); + sb.append(offset); + JSONObject params = new JSONObject(); + JSONArray array = new JSONArray(); + try (Statement prepared = conn.createStatement()) { + try (ResultSet rs = prepared.executeQuery(sb.toString())) { + params.put("srcLang", srcLang); + params.put("tgtLang", tgtLang); + params.put("caseSensitiveMatches", caseSensitiveMatches); + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String pure = rs.getString(5); + JSONObject json = new JSONObject(); + json.put("file", file); + json.put("unit", unit); + json.put("segment", segment); + json.put("pure", pure); + array.put(json); + if (array.length() == BATCHSIZE) { + break; + } + } + params.put("segments", array); + } + } + JSONArray translations = engine.batchTranslate(params); + return storeMatches(translations, memoryName, penalization); + } + + private int storeMatches(JSONArray translations, String memoryName, int penalization) + throws SAXException, IOException, ParserConfigurationException, SQLException { + int count = 0; + for (int i = 0; i < translations.length(); i++) { + JSONObject json = translations.getJSONObject(i); + JSONArray matches = json.getJSONArray("matches"); + if (matches.length() > 0) { + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + + getSegment.setString(1, file); + getSegment.setString(2, unit); + getSegment.setString(3, segment); + try (ResultSet rs = getSegment.executeQuery()) { + while (rs.next()) { + String src = rs.getString(1); + String tgt = rs.getString(2); + Element original = XliffUtils.buildElement(src); + if (tgt == null || tgt.isEmpty()) { + tgt = ""; + } + Element originalTarget = XliffUtils.buildElement(tgt); + boolean updated = false; + for (int j = 0; j < matches.length(); j++) { + Match m = new Match(matches.getJSONObject(j)); + XliffUtils.setTags(new JSONObject()); + Element matchSource = XliffUtils.toXliff(segment, j, "source", m.getSource()); + matchSource.setAttribute("xml:lang", srcLang); + Element matchTarget = XliffUtils.toXliff(segment, j, "target", m.getTarget()); + matchTarget.setAttribute("xml:lang", tgtLang); + int similarity = m.getSimilarity() - tagDifferences(original, matchSource) - penalization; + insertMatch(file, unit, segment, memoryName, Constants.TM, similarity, matchSource, + matchTarget, XliffUtils.getTags()); + if (similarity == 100 && originalTarget.getContent().isEmpty() && !updated) { + if (!matchTarget.getChildren().isEmpty()) { + matchTarget = fixTags(original, matchSource, matchTarget); + } + updateTarget(file, unit, segment, matchTarget, XliffUtils.pureText(matchTarget), false); + updated = true; + } + } + } + } + conn.commit(); + count++; + } + } + return count; + } + + private Element fixTags(Element source, Element matchSource, Element matchTarget) { + List sourceTags = source.getChildren(); + List matchSourceTags = matchSource.getChildren(); + List matchTargetTags = matchTarget.getChildren(); + for (int i = 0; i < matchTargetTags.size(); i++) { + Element targetTag = matchTargetTags.get(i); + for (int j = 0; j < matchSourceTags.size(); j++) { + Element sourceTag = matchSourceTags.get(j); + if (sourceTag.equals(targetTag)) { + if (j < sourceTags.size()) { + Element originalTag = sourceTags.get(j); + targetTag.clone(originalTag); + } + break; + } + } + } + return matchTarget; + } + + public void removeTranslations() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND translate='Y' and targetText<>'' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String src = rs.getString(4); + Element source = XliffUtils.buildElement(src); + Element target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + updateTarget(file, unit, segment, target, "", false); + } + } + } + + public void unconfirmTranslations() throws SQLException { + stmt.execute("UPDATE segments SET state='initial' WHERE type='S' AND targetText='' AND translate='Y' "); + stmt.execute("UPDATE segments SET state='translated' WHERE type='S' AND targetText <> '' AND translate='Y' "); + conn.commit(); + } + + public void pseudoTranslate() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String src = rs.getString(4); + + Element source = XliffUtils.buildElement(src); + Element target = pseudoTranslate(source); + String pureTarget = XliffUtils.pureText(target); + + updateTarget(file, unit, segment, target, pureTarget, false); + } + } + } + + private Element pseudoTranslate(Element source) { + Element target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + List content = source.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.TEXT_NODE) { + TextNode t = (TextNode) node; + target.addContent(pseudoTranslate(t.getText())); + } + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("g".equals(e.getName())) { + e.setText(pseudoTranslate(e.getText())); + } + target.addContent(e); + } + } + return target; + } + + private String pseudoTranslate(String text) { + String result = text.replace('a', '\u00E3'); + result = result.replace('e', '\u00E8'); + result = result.replace('i', '\u00EE'); + result = result.replace('o', '\u00F4'); + result = result.replace('A', '\u00C4'); + result = result.replace('E', '\u00CB'); + result = result.replace('I', '\u00CF'); + result = result.replace('O', '\u00D5'); + result = result.replace('U', '\u00D9'); + return result; + } + + public void copyAllSources() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String src = rs.getString(4); + + Element source = XliffUtils.buildElement(src); + Element target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + target.setContent(source.getContent()); + String pureTarget = XliffUtils.pureText(target); + + updateTarget(file, unit, segment, target, pureTarget, false); + } + } + } + + public void confirmAllTranslations(String memory) + throws SQLException, SAXException, IOException, ParserConfigurationException, URISyntaxException { + if (memory.equals(Constants.NONE)) { + stmt.execute("UPDATE segments SET state='final' WHERE type='S' AND targetText<>'' AND translate='Y' "); + conn.commit(); + return; + } + MemoriesHandler.open(memory); + ITmEngine engine = MemoriesHandler.getEngine(memory); + String sql = "SELECT file, unitId, segId, FROM segments WHERE state<>'final' AND type='S' AND targetText<>'' AND translate='Y'"; + try (ResultSet rs = stmt.executeQuery(sql)) { + try (PreparedStatement updateSegment = conn + .prepareStatement("UPDATE segments SET state='final' WHERE file=? AND unitId=? AND segId=?")) { + + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + + updateSegment.setString(1, file); + updateSegment.setString(2, unit); + updateSegment.setString(3, segment); + updateSegment.executeUpdate(); + + getSegment.setString(1, file); + getSegment.setString(2, unit); + getSegment.setString(3, segment); + + try (ResultSet rs2 = getSegment.executeQuery()) { + while (rs2.next()) { + Element source = XliffUtils.buildElement(rs2.getString(1)); + Element target = XliffUtils.buildElement(rs2.getString(2)); + Map tags = getTags(source); + StringBuilder key = new StringBuilder(); + key.append(xliffFile.hashCode()); + key.append('-'); + key.append(file); + key.append('-'); + key.append(unit); + key.append('-'); + key.append(segment); + engine.storeTu(XliffUtils.toTu(key.toString(), source, target, tags, srcLang, tgtLang)); + } + } + } + } + } + MemoriesHandler.close(memory); + conn.commit(); + } + + public void acceptAll100Matches() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + try (PreparedStatement perfectMatches = conn.prepareStatement( + "SELECT source, target FROM matches WHERE file=? AND unitId=? AND segId=? AND type='tm' AND similarity=100 LIMIT 1")) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String src = rs.getString(4); + Element originalSource = XliffUtils.buildElement(src); + + perfectMatches.setString(1, file); + perfectMatches.setString(2, unit); + perfectMatches.setString(3, segment); + try (ResultSet rs2 = perfectMatches.executeQuery()) { + while (rs2.next()) { + Element source = XliffUtils.buildElement(rs2.getString(1)); + String tgt = rs2.getString(2); + Element target = XliffUtils.buildElement(tgt); + target.setAttribute("xml:lang", tgtLang); + if (originalSource.hasAttribute("xml:space")) { + target.setAttribute("xml:space", originalSource.getAttributeValue("xml:space")); + } + String pureTarget = XliffUtils.pureText(target); + if (!target.getChildren().isEmpty()) { + target = fixTags(originalSource, source, target); + } + updateTarget(file, unit, segment, target, pureTarget, false); + } + } + } + } + } + } + + public String generateStatistics() + throws SQLException, SAXException, IOException, ParserConfigurationException, URISyntaxException { + getPreferences(); + updateXliff(); + File file = new File(xliffFile); + + Map map = new HashMap<>(); + Map> filesMap = new HashMap<>(); + + String sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type='S' GROUP BY file"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + JSONObject json = new JSONObject(); + int words = rs.getInt(2); + int segments = rs.getInt(3); + json.put("file", fileId); + json.put("words", words); + json.put("segments", segments); + map.put(fileId, json); + } + } + + sql = "SELECT id, name FROM files"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String id = rs.getString(1); + String name = rs.getString(2); + map.get(id).put("name", name); + if (filesMap.containsKey(name)) { + Set ids = filesMap.get(name); + ids.add(id); + filesMap.put(name, ids); + } else { + Set ids = new TreeSet<>(); + ids.add(id); + filesMap.put(name, ids); + } + } + } + + sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext = '' GROUP BY file"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + int untranslated = rs.getInt(2); + int untranslatedSegments = rs.getInt(3); + map.get(fileId).put("untranslated", untranslated); + map.get(fileId).put("untranslatedSegments", untranslatedSegments); + } + } + + sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND targettext <> '' GROUP BY file"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + int translated = rs.getInt(2); + int translatedSegments = rs.getInt(3); + map.get(fileId).put("translated", translated); + map.get(fileId).put("translatedSegments", translatedSegments); + } + } + + sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND state = 'final' GROUP BY file"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + int confirmed = rs.getInt(2); + int confirmedSegments = rs.getInt(3); + map.get(fileId).put("confirmed", confirmed); + map.get(fileId).put("confirmedSegments", confirmedSegments); + } + } + + Map statusMap = new HashMap<>(); + Set currentFileSegments = null; + Set otherFileSegments = new TreeSet<>(); + + sql = "SELECT MAX(similarity) FROM matches WHERE file = ? AND unitid = ? AND segid = ?"; + try (PreparedStatement st = conn.prepareStatement(sql)) { + String currentFileId = ""; + JSONObject json = null; + sql = "SELECT file, unitid, segid, source, words,tags FROM segments WHERE type = 'S' ORDER BY file, unitid, segid"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + String unitId = rs.getString(2); + String segId = rs.getString(3); + String source = rs.getString(4); + int words = rs.getInt(5); + int tags = rs.getInt(6); + if (!currentFileId.equals(fileId)) { + json = new JSONObject(); + json.put("newSegments", 0); + json.put("100Segments", 0); + json.put("95Segments", 0); + json.put("85Segments", 0); + json.put("75Segments", 0); + json.put("50Segments", 0); + json.put("intRepSegment", 0); + json.put("extRepSegment", 0); + json.put("newWords", 0); + json.put("100Words", 0); + json.put("95Words", 0); + json.put("85Words", 0); + json.put("75Words", 0); + json.put("50Words", 0); + json.put("tags", 0); + json.put("intRep", 0); + json.put("extRep", 0); + statusMap.put(fileId, json); + currentFileId = fileId; + if (currentFileSegments != null) { + otherFileSegments.addAll(currentFileSegments); + } + currentFileSegments = new TreeSet<>(); + } + json.put("tags", json.getInt("tags") + tags); + st.setString(1, fileId); + st.setString(2, unitId); + st.setString(3, segId); + int max = 0; + try (ResultSet rs2 = st.executeQuery()) { + while (rs2.next()) { + max = rs2.getInt(1); + } + } + if (max < 50) { + if (currentFileSegments.contains(source)) { + json.put("intRepSegment", json.getInt("intRepSegment") + 1); + json.put("intRep", json.getInt("intRep") + words); + } else if (otherFileSegments.contains(source)) { + json.put("extRepSegment", json.getInt("extRepSegment") + 1); + json.put("extRep", json.getInt("extRep") + words); + } else { + json.put("newSegments", json.getInt("newSegments") + 1); + json.put("newWords", json.getInt("newWords") + words); + } + } + currentFileSegments.add(source); + if (max == 100) { + json.put("100Segments", json.getInt("100Segments") + 1); + json.put("100Words", json.getInt("100Words") + words); + } + if (max >= 95 && max <= 99) { + json.put("95Segments", json.getInt("95Segments") + 1); + json.put("95Words", json.getInt("95Words") + words); + } + if (max >= 85 && max <= 94) { + json.put("85Segments", json.getInt("85Segments") + 1); + json.put("85Words", json.getInt("85Words") + words); + } + if (max >= 75 && max <= 84) { + json.put("75Segments", json.getInt("75Segments") + 1); + json.put("75Words", json.getInt("75Words") + words); + } + if (max >= 50 && max <= 74) { + json.put("50Segments", json.getInt("50Segments") + 1); + json.put("50Words", json.getInt("50Words") + words); + } + } + } + } + + sql = "SELECT file, SUM(words), COUNT(*) FROM segments WHERE type = 'S' AND translate = 'N' GROUP BY file"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String fileId = rs.getString(1); + int confirmed = rs.getInt(2); + int confirmedSegments = rs.getInt(3); + map.get(fileId).put("locked", confirmed); + map.get(fileId).put("lockedSegments", confirmedSegments); + } + } + + Set keys = map.keySet(); + Iterator it = keys.iterator(); + while (it.hasNext()) { + String key = it.next(); + JSONObject json = map.get(key); + if (!json.has("untranslated")) { + json.put("untranslated", 0); + map.put(key, json); + } + if (!json.has("translated")) { + json.put("translated", 0); + map.put(key, json); + } + if (!json.has("confirmed")) { + json.put("confirmed", 0); + map.put(key, json); + } + if (!json.has("translatedSegments")) { + json.put("translatedSegments", 0); + map.put(key, json); + } + if (!json.has("untranslatedSegments")) { + json.put("untranslatedSegments", 0); + map.put(key, json); + } + if (!json.has("confirmedSegments")) { + json.put("confirmedSegments", 0); + map.put(key, json); + } + if (!json.has("locked")) { + json.put("locked", 0); + map.put(key, json); + } + if (!json.has("lockedSegments")) { + json.put("lockedSegments", 0); + map.put(key, json); + } + } + + File log = new File(file.getAbsolutePath() + ".log.html"); + try (FileOutputStream out = new FileOutputStream(log)) { + + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, " \n"); + writeString(out, " Project Statistics\n"); + writeString(out, " \n"); + writeString(out, "\n"); + writeString(out, "\n"); + + writeString(out, "

" + XMLUtils.cleanText(file.getName()) + "

\n"); + + Set files = new TreeSet<>(); + files.addAll(filesMap.keySet()); + it = files.iterator(); + int count = 1; + + int projectNew = 0; + int project100 = 0; + int project95 = 0; + int project85 = 0; + int project75 = 0; + int project50 = 0; + int projectIntRep = 0; + int projectExtRep = 0; + + writeString(out, "
\n"); + writeString(out, "

" + Messages.getString("XliffStore.6") + "

\n"); + writeString(out, "

" + Messages.getString("XliffStore.7") + "

\n"); + + writeString(out, "\n"); + writeString(out, "\n"); + while (it.hasNext()) { + String fileName = it.next(); + Set set = filesMap.get(fileName); + Iterator st = set.iterator(); + + int newSegments = 0; + int intRep = 0; + int extRep = 0; + int segments100 = 0; + int segments95 = 0; + int segments85 = 0; + int segments75 = 0; + int segments50 = 0; + + while (st.hasNext()) { + String key = st.next(); + JSONObject json = statusMap.get(key); + newSegments += json.getInt("newSegments"); + intRep += json.getInt("intRepSegment"); + extRep += json.getInt("extRepSegment"); + segments100 += json.getInt("100Segments"); + segments95 += json.getInt("95Segments"); + segments85 += json.getInt("85Segments"); + segments75 += json.getInt("75Segments"); + segments50 += json.getInt("50Segments"); + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + projectNew += newSegments; + project100 += segments100; + project95 += segments95; + project85 += segments85; + project75 += segments75; + project50 += segments50; + projectIntRep += intRep; + projectExtRep += extRep; + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + + Messages.getString("XliffStore.9") + + "100%95% - 99%85% - 95%75% - 84%50% - 84%" + + Messages.getString("XliffStore.10") + "" + Messages.getString("XliffStore.11") + + "" + Messages.getString("XliffStore.12") + "
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + newSegments + "" + segments100 + "" + segments95 + "" + segments85 + "" + segments75 + "" + segments50 + "" + intRep + "" + extRep + "" + (newSegments + segments100 + segments95 + segments85 + + segments75 + segments50 + intRep + extRep) + "
 " + Messages.getString("XliffStore.12") + "" + projectNew + "" + project100 + "" + project95 + "" + project85 + "" + project75 + "" + project50 + "" + projectIntRep + "" + projectExtRep + "" + (projectNew + project100 + project95 + project85 + project75 + + project50 + projectIntRep + projectExtRep) + "
\n"); + + it = files.iterator(); + count = 1; + + projectNew = 0; + project100 = 0; + project95 = 0; + project85 = 0; + project75 = 0; + project50 = 0; + projectIntRep = 0; + projectExtRep = 0; + int projectTags = 0; + writeString(out, "

" + Messages.getString("XliffStore.13") + "

\n"); + + writeString(out, "\n"); + writeString(out, "\n"); + while (it.hasNext()) { + String fileName = it.next(); + Set set = filesMap.get(fileName); + Iterator st = set.iterator(); + + int newSegments = 0; + int intRep = 0; + int extRep = 0; + int segments100 = 0; + int segments95 = 0; + int segments85 = 0; + int segments75 = 0; + int segments50 = 0; + int tags = 0; + + while (st.hasNext()) { + String key = st.next(); + JSONObject json = statusMap.get(key); + newSegments += json.getInt("newWords"); + intRep += json.getInt("intRep"); + extRep += json.getInt("extRep"); + segments100 += json.getInt("100Words"); + segments95 += json.getInt("95Words"); + segments85 += json.getInt("85Words"); + segments75 += json.getInt("75Words"); + segments50 += json.getInt("50Words"); + tags += json.getInt("tags"); + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + projectNew += newSegments; + project100 += segments100; + project95 += segments95; + project85 += segments85; + project75 += segments75; + project50 += segments50; + projectIntRep += intRep; + projectExtRep += extRep; + projectTags += tags; + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + + Messages.getString("XliffStore.9") + + "100%95% - 99%85% - 95%75% - 84%50% - 84%" + + "Int. Rep." + "" + Messages.getString("XliffStore.16") + "" + + Messages.getString("XliffStore.17") + "" + Messages.getString("XliffStore.12") + + "
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + newSegments + "" + segments100 + "" + segments95 + "" + segments85 + "" + segments75 + "" + segments50 + "" + intRep + "" + extRep + "" + tags + "" + (newSegments + segments100 + segments95 + segments85 + + segments75 + segments50 + intRep + extRep) + "
 " + Messages.getString("XliffStore.12") + "" + projectNew + "" + project100 + "" + project95 + "" + project85 + "" + project75 + "" + project50 + "" + projectIntRep + "" + projectExtRep + "" + projectTags + "" + (projectNew + project100 + project95 + project85 + project75 + + project50 + projectIntRep + projectExtRep) + "
\n"); + + writeString(out, + "

" + Messages.getString("XliffStore.18") + " " + Messages.getString("XliffStore.19") + + "
" + Messages.getString("XliffStore.20") + " " + + Messages.getString("XliffStore.21") + + "

"); + + it = files.iterator(); + count = 1; + int projectSegments = 0; + int projectTranslatedSegments = 0; + int projectUntranslatedSegments = 0; + int projectConfirmedSegments = 0; + + writeString(out, "
\n"); + writeString(out, "

" + Messages.getString("XliffStore.22") + "

\n"); + writeString(out, "

" + Messages.getString("XliffStore.7") + "

\n"); + + writeString(out, "\n"); + writeString(out, + "\n"); + while (it.hasNext()) { + String fileName = it.next(); + Set set = filesMap.get(fileName); + Iterator st = set.iterator(); + int fileSegments = 0; + int fileTranslated = 0; + int fileUntranslated = 0; + int fileConfirmed = 0; + while (st.hasNext()) { + String key = st.next(); + JSONObject json = map.get(key); + fileSegments += json.getInt("segments"); + fileTranslated += json.getInt("translatedSegments"); + fileUntranslated += json.getInt("untranslatedSegments"); + fileConfirmed += json.getInt("confirmedSegments"); + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + projectSegments += fileSegments; + projectTranslatedSegments += fileTranslated; + projectUntranslatedSegments += fileUntranslated; + projectConfirmedSegments += fileConfirmed; + + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + + Messages.getString("XliffStore.25") + "" + Messages.getString("XliffStore.26") + + "" + Messages.getString("XliffStore.27") + "" + + Messages.getString("XliffStore.28") + "" + Messages.getString("XliffStore.12") + + "
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + fileUntranslated + "" + fileTranslated + "" + (fileSegments - fileConfirmed) + "" + fileConfirmed + "" + fileSegments + "
 Total" + projectUntranslatedSegments + "" + projectTranslatedSegments + "" + (projectSegments - projectConfirmedSegments) + "" + projectConfirmedSegments + "" + projectSegments + "
\n"); + + writeString(out, "

" + Messages.getString("XliffStore.29") + "

\n"); + + count = 1; + int projectWords = 0; + int projectTranslated = 0; + int projectUntranslated = 0; + int projectConfirmed = 0; + + writeString(out, "\n"); + writeString(out, + "\n"); + it = files.iterator(); + while (it.hasNext()) { + String fileName = it.next(); + Set set = filesMap.get(fileName); + Iterator st = set.iterator(); + int fileWords = 0; + int fileUntranslated = 0; + int fileTranslated = 0; + int fileConfirmed = 0; + while (st.hasNext()) { + String key = st.next(); + JSONObject json = map.get(key); + fileWords += json.getInt("words"); + fileTranslated += json.getInt("translated"); + fileUntranslated += json.getInt("untranslated"); + fileConfirmed += json.getInt("confirmed"); + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + projectWords += fileWords; + projectTranslated += fileTranslated; + projectUntranslated += fileUntranslated; + projectConfirmed += fileConfirmed; + } + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, ""); + writeString(out, "\n"); + writeString(out, "
#" + Messages.getString("XliffStore.8") + "" + + Messages.getString("XliffStore.25") + "" + Messages.getString("XliffStore.26") + + "" + Messages.getString("XliffStore.27") + "" + + Messages.getString("XliffStore.28") + "" + Messages.getString("XliffStore.12") + + "
" + count++ + "" + XMLUtils.cleanText(fileName) + "" + fileUntranslated + "" + fileTranslated + "" + (fileWords - fileConfirmed) + "" + fileConfirmed + "" + fileWords + "
 Total" + projectUntranslated + "" + projectTranslated + "" + (projectWords - projectConfirmed) + "" + projectConfirmed + "" + projectWords + "
\n"); + + SvgStats svgStats = new SvgStats(); + svgStats.analyse(xliffFile, catalog); + + Element matchesSvg = svgStats.generateMatchesSvg(); + Element translatedSvg = svgStats.generateTranslatedSvg(); + Element approvedSvg = svgStats.generateApprovedSvg(); + + writeString(out, "

" + Messages.getString("XliffStore.35") + "

\n"); + writeString(out, translatedSvg.toString()); + writeString(out, "\n
\n"); + + writeString(out, "

" + Messages.getString("XliffStore.36") + "

\n"); + writeString(out, approvedSvg.toString()); + writeString(out, "\n
\n"); + + writeString(out, "

" + Messages.getString("XliffStore.37") + "

\n"); + writeString(out, matchesSvg.toString()); + writeString(out, "\n
\n"); + + writeString(out, "\n"); + writeString(out, "\n"); + } + return log.getAbsolutePath(); + } + + public void replaceText(JSONObject json) + throws SQLException, SAXException, IOException, ParserConfigurationException { + String searchText = json.getString("searchText"); + String replaceText = json.getString("replaceText"); + boolean isRegExp = json.getBoolean("regExp"); + boolean caseSensitive = json.getBoolean("caseSensitive"); + StringBuilder queryBuilder = new StringBuilder(); + queryBuilder.append("SELECT file, unitId, segId, target FROM segments WHERE type='S' AND "); + if (isRegExp) { + try { + Pattern.compile(searchText); + } catch (PatternSyntaxException e) { + throw new IOException("Invalid regular expression"); + } + queryBuilder.append(" targetText REGEXP '"); + queryBuilder.append(searchText); + queryBuilder.append("' "); + } else { + queryBuilder.append(caseSensitive ? "targetText GLOB '*" : "targetText LIKE '%"); + queryBuilder.append(escape(searchText)); + queryBuilder.append(caseSensitive ? "*" : "%'"); + } + queryBuilder.append(" AND translate='Y'"); + try (ResultSet rs = stmt.executeQuery(queryBuilder.toString())) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String tgt = rs.getString(4); + + Element target = XliffUtils.buildElement(tgt); + target = replaceText(target, searchText, replaceText, isRegExp); + String pureTarget = XliffUtils.pureText(target); + updateTarget(file, unit, segment, target, pureTarget, false); + } + } + } + + private String escape(String string) { + return string.replace("'", "''").replace("%", "\\%").replace("_", "\\_"); + } + + private Element replaceText(Element target, String searchText, String replaceText, boolean isRegExp) { + List newContent = new Vector<>(); + List content = target.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.TEXT_NODE) { + String text = ((TextNode) node).getText(); + text = isRegExp ? text.replaceAll(searchText, replaceText) : text.replace(searchText, replaceText); + newContent.add(new TextNode(text)); + } + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("mrk".equals(e.getName()) || "g".equals(e.getName())) { + e = replaceText(e, searchText, replaceText, isRegExp); + } + newContent.add(e); + } + } + target.setContent(newContent); + return target; + } + + public void applyMtAll(String projectId) + throws SQLException, SAXException, IOException, ParserConfigurationException { + File projectFolder = new File(TmsServer.getProjectsFolder(), projectId); + try (FileOutputStream out = new FileOutputStream(new File(projectFolder, "applymt.xlf"))) { + String oldFile = ""; + String oldUnit = ""; + out.write(("\n") + .getBytes(StandardCharsets.UTF_8)); + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String sourceText = rs.getString(4); + + Element matchSource = XliffUtils.buildElement(sourceText); + + if (!oldFile.equals(file)) { + if (!oldFile.isEmpty()) { + out.write("\n".getBytes(StandardCharsets.UTF_8)); + } + oldFile = file; + out.write(("\n").getBytes(StandardCharsets.UTF_8)); + } + if (!oldUnit.equals(unit)) { + if (!oldUnit.isEmpty()) { + out.write("\n".getBytes(StandardCharsets.UTF_8)); + } + oldUnit = unit; + out.write(("\n").getBytes(StandardCharsets.UTF_8)); + } + Element seg = new Element("segment"); + seg.setAttribute("id", segment); + seg.addContent(matchSource); + out.write(seg.toString().getBytes(StandardCharsets.UTF_8)); + out.write("\n".getBytes(StandardCharsets.UTF_8)); + } + out.write("\n".getBytes(StandardCharsets.UTF_8)); + out.write("\n".getBytes(StandardCharsets.UTF_8)); + out.write(("").getBytes(StandardCharsets.UTF_8)); + } + } + } + + public void acceptAllMT() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' AND (state='initial' OR targetText='') AND translate='Y' "; + try (ResultSet rs = stmt.executeQuery(sql)) { + try (PreparedStatement mtMatches = conn.prepareStatement( + "SELECT target FROM matches WHERE file=? AND unitId=? AND segId=? AND type='mt' LIMIT 1")) { + while (rs.next()) { + String file = rs.getString(1); + String unit = rs.getString(2); + String segment = rs.getString(3); + String src = rs.getString(4); + + mtMatches.setString(1, file); + mtMatches.setString(2, unit); + mtMatches.setString(3, segment); + try (ResultSet rs2 = mtMatches.executeQuery()) { + while (rs2.next()) { + Element source = XliffUtils.buildElement(src); + String tgt = rs2.getString(1); + Element target = XliffUtils.buildElement(tgt); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + String pureTarget = XliffUtils.pureText(target); + updateTarget(file, unit, segment, target, pureTarget, false); + } + } + } + } + } + } + + public void removeMatches(String type) throws SQLException { + try (PreparedStatement prep = conn.prepareStatement("DELETE FROM matches WHERE type=?")) { + prep.setString(1, type); + prep.execute(); + } + conn.commit(); + } + + public JSONArray getTerms(JSONObject json) throws SQLException { + JSONArray result = new JSONArray(); + getTerms.setString(1, json.getString("file")); + getTerms.setString(2, json.getString("unit")); + getTerms.setString(3, json.getString("segment")); + try (ResultSet rs = getTerms.executeQuery()) { + while (rs.next()) { + JSONObject obj = new JSONObject(); + obj.put("termId", rs.getString(1)); + obj.put("origin", rs.getString(2)); + obj.put("source", rs.getString(3)); + obj.put("target", rs.getString(4)); + obj.put("srcLang", srcLang); + obj.put("tgtLang", tgtLang); + result.put(obj); + } + } + return sortTerms(result); + } + + public JSONArray getSegmentTerms(JSONObject json) + throws SQLException, IOException, SAXException, ParserConfigurationException, URISyntaxException { + JSONArray result = new JSONArray(); + + getPreferences(); + int similarity = fuzzyTermSearches ? 70 : 100; + + String sourceText = ""; + getSource.setString(1, json.getString("file")); + getSource.setString(2, json.getString("unit")); + getSource.setString(3, json.getString("segment")); + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + sourceText = rs.getString(2); + } + } + Language sourceLanguage = LanguageUtils.getLanguage(srcLang); + List words = sourceLanguage.isCJK() ? cjkWordList(sourceText, NGrams.TERM_SEPARATORS) + : NGrams.buildWordList(sourceText, NGrams.TERM_SEPARATORS); + + List terms = new Vector<>(); + + String glossary = json.getString("glossary"); + GlossariesHandler.openGlossary(glossary); + String glossaryName = GlossariesHandler.getGlossaryName(glossary); + ITmEngine engine = GlossariesHandler.getEngine(glossary); + Map visited = new Hashtable<>(); + for (int i = 0; i < words.size(); i++) { + StringBuilder termBuilder = new StringBuilder(); + for (int length = 0; length < MAXTERMLENGTH; length++) { + if (i + length < words.size()) { + if (!sourceLanguage.isCJK()) { + termBuilder.append(' '); + } + termBuilder.append(words.get(i + length)); + String term = termBuilder.toString().trim(); + if (!visited.containsKey(term)) { + visited.put(term, ""); + List res = engine.searchAll(term, srcLang, similarity, caseSensitiveTermSearches); + List array = parseMatches(res, glossaryName); + for (int h = 0; h < array.size(); h++) { + Term candidate = array.get(h); + if (!terms.contains(candidate)) { + terms.add(candidate); + result.put(candidate.toJSON()); + saveTerm(json.getString("file"), json.getString("unit"), json.getString("segment"), + glossaryName, candidate.getSource(), candidate.getTarget()); + } + } + } + } + } + } + GlossariesHandler.closeGlossary(glossary); + return sortTerms(result); + } + + private JSONArray sortTerms(JSONArray array) { + if (array.length() == 0) { + return array; + } + JSONArray result = new JSONArray(); + List terms = new Vector<>(); + for (int i = 0; i < array.length(); i++) { + terms.add(new Term(array.getJSONObject(i))); + } + // sort ignoring term length + Collections.sort(terms, (Term o1, Term o2) -> o1.getSource().compareToIgnoreCase(o2.getSource())); + Iterator it = terms.iterator(); + while (it.hasNext()) { + Term term = it.next(); + result.put(term.toJSON()); + } + return result; + } + + public int getProjectTerms(String glossary) + throws IOException, SQLException, SAXException, ParserConfigurationException, URISyntaxException { + getPreferences(); + Language sourceLanguage = LanguageUtils.getLanguage(srcLang); + int similarity = fuzzyTermSearches ? 70 : 100; + GlossariesHandler.openGlossary(glossary); + String glossaryName = GlossariesHandler.getGlossaryName(glossary); + ITmEngine engine = GlossariesHandler.getEngine(glossary); + int count = 0; + try (PreparedStatement segIterator = conn.prepareStatement( + "SELECT file, unitId, segId, sourceText FROM segments WHERE type='S' AND translate='Y' ")) { + try (ResultSet set = segIterator.executeQuery()) { + while (set.next()) { + String file = set.getString(1); + String unit = set.getString(2); + String segment = set.getString(3); + String sourceText = set.getString(4); + List words = sourceLanguage.isCJK() ? cjkWordList(sourceText, NGrams.TERM_SEPARATORS) + : NGrams.buildWordList(sourceText, NGrams.TERM_SEPARATORS); + Map visited = new Hashtable<>(); + boolean added = false; + for (int i = 0; i < words.size(); i++) { + StringBuilder termBuilder = new StringBuilder(); + for (int length = 0; length < MAXTERMLENGTH; length++) { + if (i + length < words.size()) { + if (!sourceLanguage.isCJK()) { + termBuilder.append(' '); + } + termBuilder.append(words.get(i + length)); + String term = termBuilder.toString().trim(); + if (!visited.containsKey(term)) { + visited.put(term, ""); + List res = engine.searchAll(term, srcLang, similarity, + caseSensitiveTermSearches); + List array = parseMatches(res, glossaryName); + for (int h = 0; h < array.size(); h++) { + Term candidate = array.get(h); + saveTerm(file, unit, segment, glossaryName, candidate.getSource(), + candidate.getTarget()); + added = true; + } + } + } + } + } + if (added) { + count++; + } + } + } + } + GlossariesHandler.closeGlossary(glossary); + return count; + } + + private void saveTerm(String file, String unit, String segment, String origin, String source, String target) + throws SQLException { + boolean found = false; + checkTerm.setString(1, file); + checkTerm.setString(2, unit); + checkTerm.setString(3, segment); + checkTerm.setString(4, "" + (source + origin).hashCode()); + try (ResultSet rs = checkTerm.executeQuery()) { + while (rs.next()) { + found = true; + } + } + if (!found) { + insertTerm.setString(1, file); + insertTerm.setString(2, unit); + insertTerm.setString(3, segment); + insertTerm.setString(4, "" + (source + origin).hashCode()); + insertTerm.setString(5, origin); + insertTerm.setString(6, source); + insertTerm.setString(7, target); + insertTerm.execute(); + conn.commit(); + } + } + + private List parseMatches(List matches, String glossaryName) { + List result = new Vector<>(); + for (int i = 0; i < matches.size(); i++) { + Map map = new Hashtable<>(); + Element tu = matches.get(i); + List tuvs = tu.getChildren("tuv"); + Iterator it = tuvs.iterator(); + while (it.hasNext()) { + Element tuv = it.next(); + map.put(tuv.getAttributeValue("xml:lang"), MemoriesHandler.pureText(tuv.getChild("seg"))); + } + if (map.containsKey(tgtLang)) { + Term term = new Term(map.get(srcLang), map.get(tgtLang), srcLang, tgtLang, glossaryName); + result.add(term); + } + } + return result; + } + + public void lockSegment(JSONObject json) throws SQLException { + String sql = "SELECT translate FROM segments WHERE file=? AND unitId=? AND segId=?"; + String segTranslate = ""; + try (PreparedStatement st = conn.prepareStatement(sql)) { + st.setString(1, json.getString("file")); + st.setString(2, json.getString("unit")); + st.setString(3, json.getString("segment")); + try (ResultSet rs = st.executeQuery()) { + while (rs.next()) { + segTranslate = rs.getString(1); + } + } + } + sql = "UPDATE segments SET translate=? WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement st = conn.prepareStatement(sql)) { + st.setString(1, segTranslate.equals("Y") ? "N" : "Y"); + st.setString(2, json.getString("file")); + st.setString(3, json.getString("unit")); + st.setString(4, json.getString("segment")); + st.executeUpdate(); + conn.commit(); + } + } + + public void unlockAll() throws SQLException { + stmt.executeUpdate("UPDATE segments SET translate='Y' WHERE type='S' AND translate='N' "); + conn.commit(); + } + + public void lockDuplicates() throws SQLException, SAXException, IOException, ParserConfigurationException { + String sql = "UPDATE segments SET translate='N' WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement lockStmt = conn.prepareStatement(sql)) { + Element currentSource = new Element("source"); + sql = "SELECT file, unitId, segId, source FROM segments WHERE type='S' ORDER BY source, file, unitId, segId"; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + Element source = XliffUtils.buildElement(rs.getString(4)); + if (source.equals(currentSource)) { + lockStmt.setString(1, rs.getString(1)); + lockStmt.setString(2, rs.getString(2)); + lockStmt.setString(3, rs.getString(3)); + lockStmt.executeUpdate(); + conn.commit(); + } else { + currentSource = source; + } + } + } + } + } + + public JSONObject analyzeSpaces() throws SQLException, IOException { + getPreferences(); + JSONObject result = new JSONObject(); + JSONArray errors = new JSONArray(); + int idx = 0; + String sql = "SELECT file, unitId, segId, child, sourceText, targetText, state, translate FROM segments WHERE type='S' ORDER BY file, child "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + idx++; + boolean segTranslate = rs.getString(8).equals("Y"); + String segState = rs.getString(7); + if (!segTranslate || Constants.INITIAL.equals(segState)) { + continue; + } + String sourceText = rs.getString(5); + String targetText = rs.getString(6); + int[] sourceSpaces = countSpaces(sourceText); + int[] targetSpaces = countSpaces(targetText); + boolean initial = sourceSpaces[0] != targetSpaces[0]; + boolean trailing = sourceSpaces[1] != targetSpaces[1]; + if (initial || trailing) { + JSONObject error = new JSONObject(); + error.put("file", rs.getString(1)); + error.put("unit", rs.getString(2)); + error.put("segment", rs.getString(3)); + String type = ""; + if (initial) { + type = Messages.getString("XliffStore.41"); + } + if (trailing) { + type = Messages.getString("XliffStore.40"); + } + if (initial && trailing) { + type = Messages.getString("XliffStore.39"); + } + error.put("type", type); + error.put("index", idx); + errors.put(error); + } + } + } + result.put("errors", errors); + return result; + } + + private int[] countSpaces(String text) { + int start = 0; + for (int i = 0; i < text.length(); i++) { + char c = text.charAt(i); + if (!Character.isWhitespace(c)) { + break; + } + start++; + } + int end = 0; + for (int i = text.length() - 1; i >= 0; i--) { + char c = text.charAt(i); + if (!Character.isWhitespace(c)) { + break; + } + end++; + } + return new int[] { start, end }; + } + + public JSONObject analyzeTags() throws SQLException, SAXException, IOException, ParserConfigurationException { + getPreferences(); + JSONObject result = new JSONObject(); + JSONArray errors = new JSONArray(); + int idx = 0; + String sql = "SELECT file, unitId, segId, child, source, target, state, translate FROM segments WHERE type='S' ORDER BY file, child "; + try (ResultSet rs = stmt.executeQuery(sql)) { + while (rs.next()) { + idx++; + boolean segTranslate = rs.getString(8).equals("Y"); + String segState = rs.getString(7); + if (!segTranslate || Constants.INITIAL.equals(segState)) { + continue; + } + String sourceText = rs.getString(5); + Element source = XliffUtils.buildElement(sourceText); + String targetText = rs.getString(6); + Element target = XliffUtils.buildElement(targetText); + + List sourceTags = tagsList(source); + List targetTags = tagsList(target); + if (sourceTags.size() > targetTags.size()) { + JSONObject error = new JSONObject(); + error.put("file", rs.getString(1)); + error.put("unit", rs.getString(2)); + error.put("segment", rs.getString(3)); + error.put("type", Messages.getString("XliffStore.38")); + error.put("index", idx); + errors.put(error); + continue; + } + if (sourceTags.size() < targetTags.size()) { + JSONObject error = new JSONObject(); + error.put("file", rs.getString(1)); + error.put("unit", rs.getString(2)); + error.put("segment", rs.getString(3)); + error.put("type", Messages.getString("XliffStore.42")); + error.put("index", idx); + errors.put(error); + continue; + } + if (sourceTags.size() == targetTags.size()) { + boolean skip = false; + for (int i = 0; i < sourceTags.size(); i++) { + String srcTag = sourceTags.get(i); + boolean found = false; + for (int j = 0; j < targetTags.size(); j++) { + String tgtTag = targetTags.get(j); + if (srcTag.equals(tgtTag)) { + found = true; + break; + } + } + if (!found) { + JSONObject error = new JSONObject(); + error.put("file", rs.getString(1)); + error.put("unit", rs.getString(2)); + error.put("segment", rs.getString(3)); + error.put("type", Messages.getString("XliffStore.43")); + error.put("index", idx); + errors.put(error); + skip = true; + } + } + if (!skip) { + for (int i = 0; i < sourceTags.size(); i++) { + if (!sourceTags.get(i).equals(targetTags.get(i))) { + JSONObject error = new JSONObject(); + error.put("file", rs.getString(1)); + error.put("unit", rs.getString(2)); + error.put("segment", rs.getString(3)); + error.put("type", Messages.getString("XliffStore.44")); + error.put("index", idx); + errors.put(error); + break; + } + } + } + } + } + } + result.put("errors", errors); + return result; + } + + private List tagsList(Element root) { + List result = new Vector<>(); + List content = root.getContent(); + Iterator it = content.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("mrk".equals(e.getName()) || "pc".equals(e.getName())) { + result.add(XliffUtils.getHeader(e)); + result.add(XliffUtils.getTail(e)); + } else { + result.add(e.toString()); + } + } + } + return result; + } + + public static List cjkWordList(String string, String separator) { + List result = new Vector<>(); + StringBuilder word = new StringBuilder(); + for (int i = 0; i < string.length(); i++) { + char c = string.charAt(i); + if (Character.isIdeographic(c)) { + if (word.length() != 0) { + result.add(word.toString()); + word.setLength(0); + } + result.add("" + c); + continue; + } + if (separator.indexOf(c) != -1) { + if (word.length() != 0) { + result.add(word.toString()); + word.setLength(0); + } + } else { + word.append(c); + } + } + if (word.length() != 0) { + result.add(word.toString()); + } + return result; + } + + public String exportHTML(String title) + throws SQLException, IOException, SAXException, ParserConfigurationException { + File output = new File(xliffFile + ".html"); + try (FileOutputStream out = new FileOutputStream(output)) { + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "" + XMLUtils.cleanText(title) + "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "

" + XMLUtils.cleanText(title) + "

\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, "\n"); + + String sourceDir = LanguageUtils.isBiDi(srcLang) ? " dir=\"rtl\"" : ""; + String targetDir = LanguageUtils.isBiDi(tgtLang) ? " dir=\"rtl\"" : ""; + + try (ResultSet rs = stmt.executeQuery( + "SELECT source, target, state, space, translate, file, child FROM segments WHERE type='S' ORDER BY file, child")) { + int count = 1; + JSONObject tagsData = new JSONObject(); + while (rs.next()) { + String src = rs.getString(1); + String tgt = rs.getString(2); + String segState = rs.getString(3); + boolean segPreserve = "Y".equals(rs.getString(4)); + boolean locked = "N".equals(rs.getString(5)); + Element source = XliffUtils.buildElement(src); + Element target = XliffUtils.buildElement(tgt); + String box = SVG_BLANK; + String border = "grey"; + if (segState.equals("translated")) { + border = "orange"; + box = SVG_TRANSLATED; + } + if (segState.equals("final")) { + border = "green"; + box = SVG_FINAL; + } + if (locked) { + box = SVG_LOCK; + } + String space = segPreserve ? "preserve" : ""; + tagsMap = new Hashtable<>(); + tag = 1; + + writeString(out, "\n"); + writeString(out, "\n"); + writeString(out, + "\n"); + writeString(out, "\n"); + writeString(out, + "\n"); + writeString(out, "\n"); + } + } + writeString(out, "
#" + LanguageUtils.getLanguage(srcLang).toString() + "" + SVG_BLANK + "" + LanguageUtils.getLanguage(tgtLang).toString() + "
" + count++ + "" + + XliffUtils.highlightSpaces( + removeSvg(addHtmlTags(source, "", false, false, tagsData, segPreserve))) + + " " + box + "" + + XliffUtils.highlightSpaces( + removeSvg(addHtmlTags(target, "", false, false, tagsData, segPreserve))) + + "
\n"); + writeString(out, "\n"); + writeString(out, ""); + } + return output.getAbsolutePath(); + } + + private static void writeString(FileOutputStream out, String string) throws IOException { + out.write(string.getBytes(StandardCharsets.UTF_8)); + } + + private static String removeSvg(String segment) { + if (segment.isEmpty()) { + return segment; + } + int index = segment.indexOf("", index) + 1; + String start = segment.substring(0, index); + String img = segment.substring(index, end); + String tag = "" + parseImg(img) + ""; + String rest = segment.substring(end); + segment = start + tag + rest; + index = segment.indexOf(" files = document.getRootElement().getChildren("file"); + for (int i = 0; i < files.size(); i++) { + Element file = files.get(i); + if (file.getAttributeValue("id").equals(currentFile)) { + List units = file.getChildren("unit"); + for (int j = 0; j < units.size(); j++) { + unit = units.get(j); + if (unit.getAttributeValue("id").equals(currentUnit)) { + List segments = unit.getChildren("segment"); + for (int k = 0; k < segments.size(); k++) { + segment = segments.get(k); + if (segment.getAttributeValue("id").equals(segmentId)) { + break; + } + } + break; + } + } + break; + } + } + + List segs = unit.getChildren("segment"); + Iterator tt = segs.iterator(); + while (tt.hasNext()) { + Element seg = tt.next(); + seg.removeChild("target"); + Element target = getTarget(currentFile, currentUnit, seg.getAttributeValue("id")); + seg.setAttribute("state", getState(currentFile, currentUnit, seg.getAttributeValue("id"))); + seg.addContent(target); + } + + Element oldSource = segment.getChild("source"); + List list1 = new Vector<>(); + List list2 = new Vector<>(); + List currentList = list1; + int visited = 0; + List sourceContent = oldSource.getContent(); + Iterator it = sourceContent.iterator(); + while (it.hasNext()) { + XMLNode node = it.next(); + if (node.getNodeType() == XMLNode.TEXT_NODE) { + TextNode textNode = (TextNode) node; + String text = textNode.getText(); + int length = text.length(); + if (length >= offset - visited && offset - visited > 0) { + String left = text.substring(0, offset - visited); + String right = text.substring(offset - visited); + currentList.add(new TextNode(left)); + currentList = list2; + currentList.add(new TextNode(right)); + } else { + currentList.add(node); + } + visited = visited + length; + } + if (node.getNodeType() == XMLNode.ELEMENT_NODE) { + Element e = (Element) node; + if ("mrk".equals(e.getName())) { + String text = e.getText(); + int length = text.length(); + if (length >= offset - visited && offset - visited > 0) { + throw new IOException(Messages.getString("XliffStore.45")); + } else { + visited = visited + length; + } + } + currentList.add(node); + } + } + + Element oldTarget = getTarget(currentFile, currentUnit, segmentId); + String pureTarget = XliffUtils.pureText(oldTarget); + + Element source1 = new Element("source"); + source1.setAttribute("xml:space", oldSource.getAttributeValue("xml:space", "default")); + source1.setContent(list1); + + Element segment1 = new Element("segment"); + segment1.setAttribute("id", segmentId + "-1"); + segment1.setAttribute("state", pureTarget.isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); + segment1.addContent(source1); + segment1.addContent(oldTarget); + + Element source2 = new Element("source"); + source2.setAttribute("xml:space", oldSource.getAttributeValue("xml:space", "default")); + source2.setContent(list2); + + Element target2 = new Element("target"); + if (oldSource.hasAttribute("xml:space")) { + target2.setAttribute("xml:space", oldSource.getAttributeValue("xml:space")); + } + + Element segment2 = new Element("segment"); + segment2.setAttribute("id", segmentId + "-2"); + segment2.setAttribute("state", Constants.INITIAL); + segment2.addContent(source2); + segment2.addContent(target2); + + List oldContent = unit.getChildren(); + List newContent = new Vector<>(); + + unit.removeChild("mtc:matches"); + unit.removeChild("gls:glossary"); + + Iterator ot = oldContent.iterator(); + while (ot.hasNext()) { + Element child = ot.next(); + if ("segment".equals(child.getName()) && segmentId.equals(child.getAttributeValue("id"))) { + newContent.add(segment1); + newContent.add(segment2); + } else { + newContent.add(child); + } + } + unit.setContent(newContent); + Indenter.indent(unit, 2); + + String sql = "SELECT MIN(child) FROM segments WHERE file=? AND unitId=?"; + index = 0; + + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, currentFile); + prep.setString(2, currentUnit); + try (ResultSet rs = prep.executeQuery()) { + while (rs.next()) { + index = rs.getInt(1); + } + } + } + + deleteUnitSegments(currentFile, currentUnit); + + sql = "UPDATE segments SET child = child + 1 WHERE file = '" + currentFile + "' AND child >= " + index; + stmt.execute(sql); + + sql = "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + insertSegmentStmt = conn.prepareStatement(sql); + + List segments = unit.getChildren(); + for (int i = 0; i < segments.size(); i++) { + Element e = segments.get(i); + if ("segment".equals(e.getName())) { + String id = e.getAttributeValue("id"); + Element source = e.getChild("source"); + boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); + Element target = e.getChild("target"); + state = e.getAttributeValue("state", + XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); + preserve = preserve || sourcePreserve + || "preserve".equals(target.getAttributeValue("xml:space", "default")); + + insertSegment(currentFile, currentUnit, id, "S", true, source, target); + } + if ("ignorable".equals(e.getName())) { + String id = e.getAttributeValue("id"); + state = ""; + insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); + } + } + insertSegmentStmt.close(); + conn.commit(); + indexSegments(); + saveXliff(); + } + + private void deleteUnitSegments(String file, String unit) throws SQLException { + String sql = "DELETE FROM segments WHERE file=? AND unitId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.execute(); + } + } + + private void deleteSegment(String file, String unit, String segment) throws SQLException { + String sql = "DELETE FROM segments WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segment); + prep.execute(); + } + + sql = "DELETE FROM matches WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segment); + prep.execute(); + } + + sql = "DELETE FROM terms WHERE file=? AND unitId=? AND segId=?"; + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, file); + prep.setString(2, unit); + prep.setString(3, segment); + prep.execute(); + } + } + + public void mergeSegment(JSONObject json) + throws SAXException, IOException, ParserConfigurationException, SQLException { + + currentFile = json.getString("file"); + currentUnit = json.getString("unit"); + String segmentId = json.getString("segment"); + + Element unit = null; + Element segment = null; + document = builder.build(xliffFile); + List files = document.getRootElement().getChildren("file"); + for (int i = 0; i < files.size(); i++) { + Element file = files.get(i); + if (file.getAttributeValue("id").equals(currentFile)) { + List units = file.getChildren("unit"); + for (int j = 0; j < units.size(); j++) { + unit = units.get(j); + if (unit.getAttributeValue("id").equals(currentUnit)) { + List segments = unit.getChildren("segment"); + for (int k = 0; k < segments.size(); k++) { + segment = segments.get(k); + if (segment.getAttributeValue("id").equals(segmentId)) { + break; + } + } + break; + } + } + break; + } + } + + List segs = unit.getChildren("segment"); + Iterator tt = segs.iterator(); + while (tt.hasNext()) { + Element seg = tt.next(); + seg.removeChild("target"); + Element target = getTarget(currentFile, currentUnit, seg.getAttributeValue("id")); + seg.setAttribute("state", getState(currentFile, currentUnit, seg.getAttributeValue("id"))); + seg.addContent(target); + } + + String sql = "SELECT MIN(child) FROM segments WHERE file=? AND unitId=?"; + index = 0; + + try (PreparedStatement prep = conn.prepareStatement(sql)) { + prep.setString(1, currentFile); + prep.setString(2, currentUnit); + try (ResultSet rs = prep.executeQuery()) { + while (rs.next()) { + index = rs.getInt(1); + } + } + } + + List oldContent = unit.getChildren(); + List newContent = new Vector<>(); + + String deletedId = null; + boolean adding = false; + Iterator ot = oldContent.iterator(); + while (ot.hasNext()) { + Element child = ot.next(); + if ("segment".equals(child.getName())) { + if (!adding) { + newContent.add(child); + } else { + segment.getChild("source").addContent(child.getChild("source").getContent()); + segment.getChild("target").addContent(child.getChild("target").getContent()); + deletedId = child.getAttributeValue("id"); + deleteSegment(currentFile, currentUnit, deletedId); + adding = false; + } + if (segmentId.equals(child.getAttributeValue("id"))) { + adding = true; + } + } else if ("ignorable".equals(child.getName())) { + if (!adding) { + newContent.add(child); + } else { + segment.getChild("source").addContent(child.getChild("source").getContent()); + Element target = child.getChild("target"); + if (target != null) { + segment.getChild("target").addContent(target.getContent()); + } + } + } else { + newContent.add(child); + } + } + unit.setContent(newContent); + if (deletedId != null) { + Element matches = unit.getChild("mtc:matches"); + if (matches != null) { + Iterator it = matches.getChildren().iterator(); + while (it.hasNext()) { + Element match = it.next(); + if (match.getAttributeValue("ref").equals("#" + deletedId)) { + match.setAttribute("ref", "#" + deletedId); + } + } + } + Element terms = unit.getChild("gls:glossary"); + if (terms != null) { + Iterator it = terms.getChildren().iterator(); + while (it.hasNext()) { + Element term = it.next(); + if (term.getAttributeValue("ref").equals("#" + deletedId)) { + term.setAttribute("ref", "#" + deletedId); + } + } + } + } + Indenter.indent(unit, 2); + + deleteUnitSegments(currentFile, currentUnit); + + sql = "INSERT INTO segments (file, unitId, segId, type, state, child, translate, tags, space, source, sourceText, target, targetText, words) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; + insertSegmentStmt = conn.prepareStatement(sql); + + List segments = unit.getChildren(); + for (int i = 0; i < segments.size(); i++) { + Element e = segments.get(i); + if ("segment".equals(e.getName())) { + String id = e.getAttributeValue("id"); + Element source = e.getChild("source"); + boolean sourcePreserve = "preserve".equals(source.getAttributeValue("xml:space", "default")); + Element target = e.getChild("target"); + if (target == null) { + target = new Element("target"); + if (source.hasAttribute("xml:space")) { + target.setAttribute("xml:space", source.getAttributeValue("xml:space")); + } + } + state = e.getAttributeValue("state", + XliffUtils.pureText(target).isEmpty() ? Constants.INITIAL : Constants.TRANSLATED); + preserve = preserve || sourcePreserve + || "preserve".equals(target.getAttributeValue("xml:space", "default")); + + insertSegment(currentFile, currentUnit, id, "S", true, source, target); + } + if ("ignorable".equals(e.getName())) { + String id = e.getAttributeValue("id"); + state = ""; + insertSegment(currentFile, currentUnit, id, "I", false, e.getChild("source"), e.getChild("target")); + } + } + + insertSegmentStmt.close(); + conn.commit(); + indexSegments(); + saveXliff(); + } + + private Element getTarget(String file, String unit, String segment) + throws SQLException, SAXException, IOException, ParserConfigurationException { + getTargetStmt.setString(1, file); + getTargetStmt.setString(2, unit); + getTargetStmt.setString(3, segment); + String tgt = ""; + try (ResultSet rs = getTargetStmt.executeQuery()) { + while (rs.next()) { + tgt = rs.getString(1); + state = rs.getString(2); + } + } + if (tgt == null || tgt.isEmpty()) { + return new Element("target"); + } + return XliffUtils.buildElement(tgt); + } + + private String getState(String file, String unit, String segment) throws SQLException { + getTargetStmt.setString(1, file); + getTargetStmt.setString(2, unit); + getTargetStmt.setString(3, segment); + String result = ""; + try (ResultSet rs = getTargetStmt.executeQuery()) { + while (rs.next()) { + result = rs.getString(2); + } + } + return result; + } + + private boolean isTranslatable(String file, String unit, String segment) throws SQLException { + getSource.setString(1, file); + getSource.setString(2, unit); + getSource.setString(3, segment); + boolean translate = true; + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + translate = rs.getString(4).equals("Y"); + } + } + return translate; + } + + private void indexSegments() throws SQLException { + int idx = 0; + String update = "UPDATE segments SET idx=? WHERE file=? AND unitID=? AND segId=?"; + try (PreparedStatement prep = conn.prepareStatement(update)) { + try (Statement st = conn.createStatement()) { + String sql = "SELECT file, unitId, segId, child FROM segments WHERE type='S' ORDER BY file, child"; + try (ResultSet rs = st.executeQuery(sql)) { + while (rs.next()) { + prep.setInt(1, idx++); + prep.setString(2, rs.getString(1)); + prep.setString(3, rs.getString(2)); + prep.setString(4, rs.getString(3)); + prep.executeUpdate(); + } + } + } + } + conn.commit(); + } + + public JSONObject getSegmentSource(JSONObject json) + throws JSONException, SQLException, SAXException, IOException, ParserConfigurationException { + getSource.setString(1, json.getString("file")); + getSource.setString(2, json.getString("unit")); + getSource.setString(3, json.getString("segment")); + String src = ""; + try (ResultSet rs = getSource.executeQuery()) { + while (rs.next()) { + src = rs.getString(1); + } + } + Element source = XliffUtils.buildElement(src); + String plainText = XliffUtils.pureText(source); + JSONObject result = new JSONObject(); + result.put("source", source.toString()); + result.put("plainText", "" + plainText + ""); + return result; + } + + public void setMTMatches(JSONObject json) + throws SQLException, IOException, JSONException, SAXException, ParserConfigurationException { + String file = json.getString("file"); + String unit = json.getString("unit"); + String segment = json.getString("segment"); + JSONArray translations = json.getJSONArray("translations"); + for (int i = 0; i < translations.length(); i++) { + JSONObject translation = translations.getJSONObject(i); + Element source = XliffUtils.buildElement(translation.getString("source")); + Element target = XliffUtils.buildElement(translation.getString("target")); + String origin = translation.getString("origin"); + insertMatch(file, unit, segment, origin, Constants.MT, 0, source, target, new JSONObject()); + } + } } \ No newline at end of file diff --git a/src/com/maxprograms/swordfish/xliff/XliffUtils.java b/src/com/maxprograms/swordfish/xliff/XliffUtils.java index d0652b1..174f0fe 100644 --- a/src/com/maxprograms/swordfish/xliff/XliffUtils.java +++ b/src/com/maxprograms/swordfish/xliff/XliffUtils.java @@ -47,8 +47,8 @@ public class XliffUtils { public static final String STYLE = "class='highlighted'"; - private static final String NOTXLIFF = "Selected file is not an XLIFF document"; - private static final String NOTSWORDFISH = "Selected file is not a Swordfish project"; + private static final String NOTXLIFF = Messages.getString("XliffUtils.0"); + private static final String NOTSWORDFISH = Messages.getString("XliffUtils.1"); private static int maxTag = 0; private static JSONObject tags; diff --git a/src/com/maxprograms/swordfish/xliff/xliff.properties b/src/com/maxprograms/swordfish/xliff/xliff.properties new file mode 100644 index 0000000..0319d32 --- /dev/null +++ b/src/com/maxprograms/swordfish/xliff/xliff.properties @@ -0,0 +1,41 @@ +Split.0=Selected file is not an XLIFF document +Split.1= without "original" attribute +XliffStore.0=Project {0} needs upgrade +XliffStore.1=Creating database +XliffStore.10=Int. Rep. +XliffStore.11=Ext. Rep. +XliffStore.12=Total +XliffStore.13=Words +XliffStore.16=Ext. Rep. +XliffStore.17=Tags +XliffStore.18=Int. Rep. +XliffStore.19=Internal Repetition = Segment repetitions within one document +XliffStore.2=Closed store +XliffStore.20=Ext. Rep. +XliffStore.21=External Repetition = Segment repetitions between all documents +XliffStore.22=Translation Status +XliffStore.25=Not Translated +XliffStore.26=Translated +XliffStore.27=Not Confirmed +XliffStore.28=Confirmed +XliffStore.29=Words +XliffStore.3=Segments: {0}\u00A0\u00A0\u00A0Words: {1}\u00A0\u00A0\u00A0Translated: {2}\u00A0\u00A0\u00A0Confirmed: {3} +XliffStore.35=Translated Segments +XliffStore.36=Approved Segments +XliffStore.37=TM Matches Quality +XliffStore.38=Missing Tag +XliffStore.39=Initial - Trailing +XliffStore.4=Changing segment state from 'initial' to 'translated' +XliffStore.40=Trailing +XliffStore.41=Initial +XliffStore.42=Extra Tags +XliffStore.43=Different Tag +XliffStore.44=Tags in wrong order +XliffStore.45=Can't split segment in locked text section. +XliffStore.5=Error assembling matches: {0}\n{1} +XliffStore.6=TM & Repetition Analysis +XliffStore.7=Segments +XliffStore.8=Document +XliffStore.9=New +XliffUtils.0=Selected file is not an XLIFF document +XliffUtils.1=Selected file is not a Swordfish project diff --git a/src/module-info.java b/src/module-info.java index 5a1b94f..9cbcc85 100644 --- a/src/module-info.java +++ b/src/module-info.java @@ -30,4 +30,5 @@ requires transitive jdk.httpserver; requires transitive json; requires java.logging; + requires org.xerial.sqlitejdbc; } \ No newline at end of file diff --git a/swordfish.pdf b/swordfish.pdf index 767dd8d..462715f 100644 Binary files a/swordfish.pdf and b/swordfish.pdf differ diff --git a/ts/Swordfish.ts b/ts/Swordfish.ts index f89e803..7409928 100644 --- a/ts/Swordfish.ts +++ b/ts/Swordfish.ts @@ -477,7 +477,7 @@ export class Swordfish { Swordfish.destroyWindow(Swordfish.concordanceSearchWindow); }); ipcMain.on('get-concordance', (event: IpcMainEvent, arg: any) => { - Swordfish.concordanceSearch(arg); + Swordfish.concordanceSearch(event, arg); }); ipcMain.on('get-selection', (event: IpcMainEvent) => { Swordfish.selectionRequest = event; @@ -2659,7 +2659,7 @@ export class Swordfish { static importTmxFile(arg: any): void { Swordfish.destroyWindow(Swordfish.importTmxWindow); Swordfish.mainWindow.webContents.send('start-waiting'); - Swordfish.mainWindow.webContents.send('set-status', 'Importing TMX'); + Swordfish.mainWindow.webContents.send('set-status', 'Importing TMX File'); Swordfish.sendRequest('/memories/import', arg, (data: any) => { if (data.status !== Swordfish.SUCCESS) { @@ -2670,6 +2670,9 @@ export class Swordfish { Swordfish.currentStatus = data; let processId: string = data.process; let intervalObject = setInterval(() => { + if (Swordfish.currentStatus.imported) { + Swordfish.mainWindow.webContents.send('set-status', 'Imported ' + Swordfish.currentStatus.imported + ' units'); + } if (Swordfish.currentStatus.status === Swordfish.SUCCESS) { if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) { Swordfish.mainWindow.webContents.send('end-waiting'); @@ -2687,7 +2690,7 @@ export class Swordfish { return; } Swordfish.getMemoriesProgress(processId); - }, 500); + }, 2500); }, (reason: string) => { Swordfish.showMessage({ type: 'error', message: reason }); @@ -4320,10 +4323,12 @@ export class Swordfish { Swordfish.setLocation(this.concordanceSearchWindow, 'concordanceSearch.html'); } - static concordanceSearch(arg: any): void { + static concordanceSearch(event: IpcMainEvent, arg: any): void { + event.sender.send('start-waiting'); Swordfish.sendRequest('/memories/concordance', arg, (data: any) => { if (data.status !== Swordfish.SUCCESS) { + event.sender.send('end-waiting'); Swordfish.showMessage({ type: 'error', message: data.reason }); return; } @@ -4332,16 +4337,14 @@ export class Swordfish { let intervalObject = setInterval(() => { if (Swordfish.currentStatus.status === Swordfish.SUCCESS) { if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) { - Swordfish.mainWindow.webContents.send('end-waiting'); - Swordfish.mainWindow.webContents.send('set-status', ''); clearInterval(intervalObject); Swordfish.concordanceResults(Swordfish.currentStatus); + event.sender.send('end-waiting'); return; } } if (Swordfish.currentStatus.status === Swordfish.ERROR) { - Swordfish.mainWindow.webContents.send('end-waiting'); - Swordfish.mainWindow.webContents.send('set-status', ''); + event.sender.send('end-waiting'); clearInterval(intervalObject); Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason }); return; @@ -4350,6 +4353,7 @@ export class Swordfish { }, 500); }, (reason: string) => { + event.sender.send('end-waiting'); Swordfish.showMessage({ type: 'error', message: reason }); } ); @@ -5287,7 +5291,6 @@ export class Swordfish { static startup(): void { Swordfish.spellCheckerLanguages = Swordfish.mainWindow.webContents.session.availableSpellCheckerLanguages; - Swordfish.checkUpdates(true); if (Swordfish.currentPreferences.srcLang === 'none') { Swordfish.getDefaultLanguages(); } @@ -5303,6 +5306,135 @@ export class Swordfish { message: 'You are running a version for Macs with Intel processors on a Mac with Apple chipset.' }); } + setTimeout(() => { + if (Swordfish.needsUpdate()) { + dialog.showMessageBox(Swordfish.mainWindow, { + type: 'question', + message: 'Swordfish needs to upgrade its databases. This may take several minutes. Do you want to proceed?', + buttons: ['Yes', 'No'] + }).then((selection: Electron.MessageBoxReturnValue) => { + if (selection.response === 0) { + Swordfish.updateDatabases(); + Swordfish.checkUpdates(true); + } else { + Swordfish.showMessage({ + type: 'warning', + message: 'You can only work on new projects, memories, and glossaries until old databases are upgraded.' + }); + Swordfish.checkUpdates(true); + } + }); + } else { + Swordfish.checkUpdates(true); + } + }, 2000); + } + + static needsUpdate(): boolean { + let projectsFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'projects'); + if (existsSync(projectsFolder)) { + let projectsList: string = Swordfish.path.join(projectsFolder, 'projects.json'); + if (existsSync(projectsList)) { + let projectsJson: any = JSON.parse(readFileSync(projectsList, 'utf8')); + let projects: any[] = projectsJson.projects; + for (let project of projects) { + let projectFolder: string = Swordfish.path.join(projectsFolder, project.id); + let dbFile: string = Swordfish.path.join(projectFolder, 'h2data'); + let sqlite: string = Swordfish.path.join(projectFolder, 'sqlite'); + if (!existsSync(sqlite) && existsSync(dbFile)) { + return true; + } + } + } + } + let memoriesFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'memories'); + if (existsSync(memoriesFolder)) { + let memoriesList: string = Swordfish.path.join(memoriesFolder, 'memories.json'); + if (existsSync(memoriesList)) { + let memoriesJson: any = JSON.parse(readFileSync(memoriesList, 'utf8')); + let keys: string[] = Object.keys(memoriesJson); + keys.forEach((key: string) => { + let memoryFolder: string = Swordfish.path.join(memoriesFolder, key); + let dbFile: string = Swordfish.path.join(memoryFolder, 'db.mv.db'); + if (existsSync(dbFile)) { + return true; + } + }); + } + } + let glossariesFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'glossaries'); + if (existsSync(glossariesFolder)) { + let glossariesList: string = Swordfish.path.join(glossariesFolder, 'glossaries.json'); + if (existsSync(glossariesList)) { + let glossariesJson: any = JSON.parse(readFileSync(glossariesList, 'utf8')); + let keys: string[] = Object.keys(glossariesJson); + keys.forEach((key: string) => { + let glossaryFolder: string = Swordfish.path.join(glossariesFolder, key); + let dbFile: string = Swordfish.path.join(glossaryFolder, 'db.mv.db'); + if (existsSync(dbFile)) { + return true; + } + }); + } + } + return false; + } + + static updateDatabases(): void { + let javapath: string = Swordfish.path.join(app.getAppPath(), 'bin', 'java'); + if (process.platform === 'win32') { + javapath = Swordfish.path.join(app.getAppPath(), 'bin', 'java.exe'); + } + let projectsFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'projects'); + if (existsSync(projectsFolder)) { + let projectsList: string = Swordfish.path.join(projectsFolder, 'projects.json'); + if (existsSync(projectsList)) { + let projectsJson: any = JSON.parse(readFileSync(projectsList, 'utf8')); + let projects: any[] = projectsJson.projects; + for (let project of projects) { + let projectFolder: string = Swordfish.path.join(projectsFolder, project.id); + let dbFile: string = Swordfish.path.join(projectFolder, 'h2data'); + let sqlite: string = Swordfish.path.join(projectFolder, 'sqlite'); + if (!existsSync(sqlite) && existsSync(dbFile)) { + Swordfish.mainWindow.webContents.send('set-status', 'Upgrading project ' + project.description); + execFileSync(javapath, ['-cp', 'lib/h2-1.4.200.jar', '--module-path', 'lib', '-m', 'swordfish/com.maxprograms.swordfish.DbUpgrade', '-project', projectFolder], { cwd: app.getAppPath(), windowsHide: true }); + } + } + } + } + let memoriesFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'memories'); + if (existsSync(memoriesFolder)) { + let memoriesList: string = Swordfish.path.join(memoriesFolder, 'memories.json'); + if (existsSync(memoriesList)) { + let memoriesJson: any = JSON.parse(readFileSync(memoriesList, 'utf8')); + let keys: string[] = Object.keys(memoriesJson); + keys.forEach((key: string) => { + let memoryFolder: string = Swordfish.path.join(memoriesFolder, key); + let dbFile: string = Swordfish.path.join(memoryFolder, 'db.mv.db'); + if (existsSync(dbFile)) { + Swordfish.mainWindow.webContents.send('set-status', 'Upgrading memory ' + memoriesJson[key].name); + execFileSync(javapath, ['-cp', 'lib/h2-1.4.200.jar', '--module-path', 'lib', '-m', 'swordfish/com.maxprograms.swordfish.DbUpgrade', '-memory', memoryFolder], { cwd: app.getAppPath(), windowsHide: true }); + } + }); + } + } + let glossariesFolder: string = Swordfish.path.join(app.getPath('appData'), app.name, 'glossaries'); + if (existsSync(glossariesFolder)) { + let glossariesList: string = Swordfish.path.join(glossariesFolder, 'glossaries.json'); + if (existsSync(glossariesList)) { + let glossariesJson: any = JSON.parse(readFileSync(glossariesList, 'utf8')); + let keys: string[] = Object.keys(glossariesJson); + keys.forEach((key: string) => { + let glossaryFolder: string = Swordfish.path.join(glossariesFolder, key); + let dbFile: string = Swordfish.path.join(glossaryFolder, 'db.mv.db'); + if (existsSync(dbFile)) { + Swordfish.mainWindow.webContents.send('set-status', 'Upgrading glossary ' + glossariesJson[key].name); + execFileSync(javapath, ['-cp', 'lib/h2-1.4.200.jar', '--module-path', 'lib', '-m', 'swordfish/com.maxprograms.swordfish.DbUpgrade', '-memory', glossaryFolder], { cwd: app.getAppPath(), windowsHide: true }); + } + }); + } + } + Swordfish.mainWindow.webContents.send('set-status', ''); } } diff --git a/ts/concordanceSearch.ts b/ts/concordanceSearch.ts index 03dcf5c..62ccd5a 100644 --- a/ts/concordanceSearch.ts +++ b/ts/concordanceSearch.ts @@ -44,6 +44,14 @@ class ConcordanceSearch { this.electron.ipcRenderer.on('set-selected-text', (event: Electron.IpcRendererEvent, arg: any) => { this.setParams(arg); }); + let regExp: HTMLInputElement = document.getElementById('regularExpression') as HTMLInputElement; + regExp.addEventListener('change', () => { + let caseSensitive: HTMLInputElement = document.getElementById('caseSensitive') as HTMLInputElement; + caseSensitive.disabled = regExp.checked; + if (regExp.checked) { + caseSensitive.checked = false; + } + }); (document.getElementById('maxEntries') as HTMLSelectElement).value = '20'; (document.getElementById('searchText') as HTMLInputElement).focus(); document.getElementById('languagesSelect').addEventListener('change', () => { @@ -52,6 +60,12 @@ class ConcordanceSearch { (document.getElementById('searchText') as HTMLInputElement).dir = 'rtl'; } }); + this.electron.ipcRenderer.on('start-waiting', (event: Electron.IpcRendererEvent, arg: any) => { + document.body.classList.add("wait"); + }); + this.electron.ipcRenderer.on('end-waiting', (event: Electron.IpcRendererEvent, arg: any) => { + document.body.classList.remove("wait"); + }); this.electron.ipcRenderer.send('concordance-search-height', { width: document.body.clientWidth, height: document.body.clientHeight }); } diff --git a/ts/filterSegments.ts b/ts/filterSegments.ts index 30d2eb1..3b474f5 100644 --- a/ts/filterSegments.ts +++ b/ts/filterSegments.ts @@ -34,6 +34,14 @@ class FilterSegments { this.clearFilter(); } }); + let regExp: HTMLInputElement = document.getElementById('isRegExp') as HTMLInputElement; + regExp.addEventListener('change', () => { + let caseSensitive: HTMLInputElement = document.getElementById('caseSensitive') as HTMLInputElement; + caseSensitive.disabled = regExp.checked; + if (regExp.checked) { + caseSensitive.checked = false; + } + }); (document.getElementById('filterSegments') as HTMLButtonElement).addEventListener('click', () => { this.filterSegments(); }); @@ -91,8 +99,14 @@ class FilterSegments { (document.getElementById('filterText') as HTMLInputElement).value = arg.filterText; (document.getElementById('source') as HTMLInputElement).checked = (arg.filterLanguage === 'source'); (document.getElementById('target') as HTMLInputElement).checked = (arg.filterLanguage === 'target'); - (document.getElementById('caseSensitive') as HTMLInputElement).checked = arg.caseSensitiveFilter; (document.getElementById('isRegExp') as HTMLInputElement).checked = arg.regExp; + let caseSensitive: HTMLInputElement = document.getElementById('caseSensitive') as HTMLInputElement; + caseSensitive.disabled = arg.regExp; + if (arg.regExp) { + caseSensitive.checked = false; + } else { + caseSensitive.checked = arg.caseSensitiveFilter; + } (document.getElementById('showUntranslated') as HTMLInputElement).checked = arg.showUntranslated; (document.getElementById('showTranslated') as HTMLInputElement).checked = arg.showTranslated; (document.getElementById('showConfirmed') as HTMLInputElement).checked = arg.showConfirmed; diff --git a/ts/preferencesDialog.ts b/ts/preferencesDialog.ts index a5d5c0a..8db1862 100644 --- a/ts/preferencesDialog.ts +++ b/ts/preferencesDialog.ts @@ -246,6 +246,12 @@ class PreferencesDialog { this.enableChatGPT.checked = preferences.chatGpt.enabled; this.chatGPTKey.value = preferences.chatGpt.apiKey; this.chatGPTModel.value = preferences.chatGpt.model; + this.chatGPTKey.disabled = !preferences.chatGpt.enabled; + this.chatGPTModel.disabled = !preferences.chatGpt.enabled; + this.enableChatGPT.addEventListener('change', () => { + this.chatGPTKey.disabled = !this.enableChatGPT.checked; + this.chatGPTModel.disabled = !this.enableChatGPT.checked; + }); this.enableModernmt.checked = preferences.modernmt.enabled; this.modernmtKey.value = preferences.modernmt.apiKey; @@ -1298,7 +1304,7 @@ class PreferencesDialog { td = document.createElement('td'); td.classList.add('middle'); td.classList.add('fill_width'); - td.innerHTML = ''; + td.innerHTML = ''; tr.appendChild(td); this.enableChatGPT = document.getElementById('enableChatGPT') as HTMLInputElement;