diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..5adf289 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,41 @@ +# Handle line endings automatically for files detected as text +# and leave all files detected as binary untouched. +* text=auto + +# +# The above will handle all files NOT found below +# +# These files are text and should be normalized (Convert crlf => lf) +*.bash text eol=lf +*.css text diff=css +*.df text +*.htm text diff=html +*.html text diff=html +*.java text diff=java +*.js text +*.json text +*.jsp text +*.jspf text +*.jspx text +*.properties text +*.sh text eol=lf +*.tld text +*.txt text +*.tag text +*.tagx text +*.xml text +*.yml text + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.dll binary +*.ear binary +*.gif binary +*.ico binary +*.jar binary +*.jpg binary +*.jpeg binary +*.png binary +*.so binary +*.war binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8858d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,204 @@ +.gradle +**/build/ +!src/**/build/ +target/ + +# Taken from https://github.com/github/gitignore + +### Java +# Compiled class file +*.class + +# Log file +*.log + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +### Eclipse +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +### JetBrains +.idea + +# Gradle and Maven with auto-import +*.iml +*.ipr + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### Vim +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +### MacOs +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Linux +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Include gradle-wrapper +!gradle/wrapper/gradle-wrapper.jar diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..00f2260 --- /dev/null +++ b/build.gradle @@ -0,0 +1,65 @@ +plugins { + id 'application' + id ("org.openjfx.javafxplugin").version("0.1.0") +} + +sourceCompatibility = 17 +version = '1.0.0' +compileJava.options.encoding = 'UTF-8' + +repositories { + mavenCentral() +} + + +// We work with the javafx version mapping the toolChain version. +// Attention: 17.0.8 works on mac, 17.0.2 not anymore (13.9.2024) +javafx { + version = "17.0.8" + modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.media' ] +} + +// Apply a specific Java toolchain to ease working on different environments. +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +dependencies { + // https://mvnrepository.com/artifact/org.slf4j/slf4j-api + implementation("org.slf4j:slf4j-api:2.0.17") + implementation ("org.slf4j:slf4j-simple:2.0.9") + +// testImplementation 'de.tum.in.ase:artemis-java-test-sandbox:1.13.0' +} + + +application { + // Define the main class for the application. +// mainClass = "prog.ex15.monolingual.gui.MonolingualWelcomeLauncher" + mainClass = "prog.ex15.solution.i18ncountries.gui.MultilingualWelcomeLauncher" +} + + +sourceSets { + main { + java { + srcDir 'src' + } + resources { + srcDirs = ['resources'] + } + } + test { + java { + srcDir 'testsrc' + } + } +} + + +tasks.named('test') { + // Use JUnit Platform for unit tests. + useJUnitPlatform() +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e18bc25 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..f3b75f3 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..9d21a21 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/resources/bundles/compoundMessages_US.properties b/resources/bundles/compoundMessages_US.properties new file mode 100644 index 0000000..1fe5600 --- /dev/null +++ b/resources/bundles/compoundMessages_US.properties @@ -0,0 +1,2 @@ + +people.have.seen.our.website.since={0} People have seen our website since {1} diff --git a/resources/bundles/i18ncountries.properties b/resources/bundles/i18ncountries.properties new file mode 100644 index 0000000..8d51393 --- /dev/null +++ b/resources/bundles/i18ncountries.properties @@ -0,0 +1,13 @@ +categories.FOOD=Food +categories.HOLIDAYS=Holidays +categories.STATISTICS=Statistics +categories.TRAFFIC=Traffic +country.DENMARK=Denmark +country.ENGLAND=England +country.GERMANY=Germany +country.NETHERLANDS=Netherlands +food.most.prominent.food=Our most prominent food is {0}. +holiday.most.important.holiday=Our most important holiday is {0} on {1}}. +statistics.population=Our population is {0}. +traffic.maximum.speed.highways=Maximum speed on highways is {0} {1}. + diff --git a/resources/bundles/i18ncountries_da_DK.properties b/resources/bundles/i18ncountries_da_DK.properties new file mode 100644 index 0000000..223f877 --- /dev/null +++ b/resources/bundles/i18ncountries_da_DK.properties @@ -0,0 +1,13 @@ +categories.FOOD=Spise +categories.HOLIDAYS=Helligdage +categories.STATISTICS=Statistikker +categories.TRAFFIC=Trafik +country.DENMARK=Danmark +country.ENGLAND=England +country.GERMANY=Tyskland +country.NETHERLANDS=Holland +food.most.prominent.food=Vores mest fremtrædende mad er {0}. +holiday.most.important.holiday=Vores vigtigste ferie er {0} på {1}. +statistics.population=Vores befolkning er {0}. +traffic.maximum.speed.highways=Den maksimale hastighed på vores motorveje er {0} {1}. + diff --git a/resources/bundles/i18ncountries_de_DE.properties b/resources/bundles/i18ncountries_de_DE.properties new file mode 100644 index 0000000..27460f9 --- /dev/null +++ b/resources/bundles/i18ncountries_de_DE.properties @@ -0,0 +1,15 @@ +categories.FOOD=Essen +categories.HOLIDAYS=Feiertage +categories.STATISTICS=Statistiken +categories.TRAFFIC=Verkehr +country.DENMARK=Dänemark +country.ENGLAND=England +country.GERMANY=Deutschland +country.NETHERLANDS=Niederlande +food.most.prominent.food=Unser bekanntestes Essen ist {0}. +holiday.most.important.holiday=Der wichtigste Feiertag ist {0} am {1}. +statistics.population=Unsere Bevölkerung beträgt {0}. +traffic.maximum.speed.highways=Die Richtgeschwindigkeit beträgt {0} {1}. Es gibt kein generelles \ + Tempolimit. + + diff --git a/resources/bundles/i18ncountries_en_GB.properties b/resources/bundles/i18ncountries_en_GB.properties new file mode 100644 index 0000000..77d20db --- /dev/null +++ b/resources/bundles/i18ncountries_en_GB.properties @@ -0,0 +1,13 @@ +categories.FOOD=Food +categories.HOLIDAYS=Holidays +categories.STATISTICS=Statistics +categories.TRAFFIC=Traffic +country.DENMARK=Denmark +country.ENGLAND=England +country.GERMANY=Germany +country.NETHERLANDS=Netherlands +food.most.prominent.food=Our most prominent food is {0}. +holiday.most.important.holiday=Our most important holiday is {0} on {1}. +statistics.population=Our population is {0}. +traffic.maximum.speed.highways=Maximum speed on highways is {0} {1}. + diff --git a/resources/bundles/i18ncountries_nl_NL.properties b/resources/bundles/i18ncountries_nl_NL.properties new file mode 100644 index 0000000..5dc56ac --- /dev/null +++ b/resources/bundles/i18ncountries_nl_NL.properties @@ -0,0 +1,13 @@ +categories.FOOD=Eten +categories.HOLIDAYS=Feestdagen +categories.STATISTICS=Statistieken +categories.TRAFFIC=Verkeer +country.DENMARK=Denemarken +country.ENGLAND=Engeland +country.GERMANY=Duitsland +country.NETHERLANDS=Nederland +food.most.prominent.food=Ons meest prominente eten zijn {0}. +holiday.most.important.holiday=Onze belangrijkste feestdag is {0} op {1}. +statistics.population=Onze bevolking is {0}. +traffic.maximum.speed.highways=De maximale snelheid op onze snelwegen is {0} {1}. + diff --git a/resources/simplelogger.properties b/resources/simplelogger.properties new file mode 100644 index 0000000..d7107f6 --- /dev/null +++ b/resources/simplelogger.properties @@ -0,0 +1,8 @@ +org.slf4j.simpleLogger.dateTimeFormat=HH:mm:ss.SSS +org.slf4j.simpleLogger.defaultLogLevel=debug +org.slf4j.simpleLogger.levelInBrackets=true +org.slf4j.simpleLogger.logFile=System.out +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.showLogName=true +org.slf4j.simpleLogger.showThreadName=true +org.slf4j.simpleLogger.showShortLogName=true diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..ebc476d --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'Welcome-to-my-Country' diff --git a/src/prog/ex15/exercise/i18ncountries/Category.java b/src/prog/ex15/exercise/i18ncountries/Category.java new file mode 100644 index 0000000..daf3761 --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/Category.java @@ -0,0 +1,8 @@ +package prog.ex15.exercise.i18ncountries; + +/** + * Categories for the country related knowledge. + */ +public enum Category { + TRAFFIC, FOOD, HOLIDAYS, STATISTICS +} diff --git a/src/prog/ex15/exercise/i18ncountries/Configuration.java b/src/prog/ex15/exercise/i18ncountries/Configuration.java new file mode 100644 index 0000000..c882d13 --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/Configuration.java @@ -0,0 +1,61 @@ +package prog.ex15.exercise.i18ncountries; + +import java.beans.PropertyChangeListener; +import java.util.Locale; +import java.util.Map; +import java.util.ResourceBundle; + +/** + * Facade to a configuration class which simplifies I18N. + */ +public interface Configuration { + + /** + * Returns the current Locale object. + * + * @return current Locale object + */ + Locale getLocale(); + + /** + * Sets the locale to a new value. This triggers that the ResourceBundles get reloaded. + * + * @param locale new locale + */ + void setLocale(Locale locale); + + /** + * Returns the current ListResourceBundle object. + * + * @return current ListResourceBundle object + */ + ResourceBundle getTypicalBundle(); + + /** + * Returns the current PropertyResourceBundle object. + * + * @return current PropertyResourceBundle object + */ + ResourceBundle getMessageBundle(); + + /** + * Returns a map which associates the possible countries with their Locale objects. + * + * @return map with the associations of Country to Locale + */ + Map getCountry2LocaleMap(); + + /** + * Adds a PropertyChangeListener to the configuration object. + * + * @param listener listener to be added + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Removes a PropertyChangeListener from the configuration object. + * + * @param listener listener to be removed + */ + void removePropertyChangeListener(PropertyChangeListener listener); +} diff --git a/src/prog/ex15/exercise/i18ncountries/Country.java b/src/prog/ex15/exercise/i18ncountries/Country.java new file mode 100644 index 0000000..acd7313 --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/Country.java @@ -0,0 +1,8 @@ +package prog.ex15.exercise.i18ncountries; + +/** + * Countries for the WelcomeToMyCountry project. + */ +public enum Country { + GERMANY, ENGLAND, NETHERLANDS, DENMARK +} diff --git a/src/prog/ex15/exercise/i18ncountries/CountryKnowledgeContainer.java b/src/prog/ex15/exercise/i18ncountries/CountryKnowledgeContainer.java new file mode 100644 index 0000000..59f9e47 --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/CountryKnowledgeContainer.java @@ -0,0 +1,66 @@ +package prog.ex15.exercise.i18ncountries; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * This container holds knowledge about a country related to different categories. + */ +public class CountryKnowledgeContainer { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(CountryKnowledgeContainer.class); + + Map> categoryKnowledgeMap; + + /** + * Creates an empty container. + */ + public CountryKnowledgeContainer() { + clear(); + } + + /** + * Adds a new entry to the container. + * + * @param category Category the knowledge is related to. The category must be NotNull. + * @param knowledge Knowledge string. The string must be NotNull. + * @throws IllegalArgumentException if either the category or the string is null + */ + public void addKnowledge(Category category, String knowledge) throws IllegalArgumentException { + if (category == null) { + throw new IllegalArgumentException("category is null reference."); + } + if (knowledge == null) { + throw new IllegalArgumentException("knowledge is null reference."); + } + List knowledgeList = categoryKnowledgeMap.get(category); + knowledgeList.add(knowledge); + } + + /** + * Returns a List of strings containing the knowledge, related to the given category. + * + * @param category selected category. The category must be NotNull. + * @return List of strings containing the knowledge, related to the given category + * @throws IllegalArgumentException if the category is null + */ + public List getKnowledge(Category category) throws IllegalArgumentException { + if (category == null) { + throw new IllegalArgumentException("category is null reference."); + } + return categoryKnowledgeMap.get(category); + } + + /** + * (Re)sets the data structures of the container. + */ + public void clear() { + categoryKnowledgeMap = new HashMap<>(); + + for (Category category : Category.values()) { + categoryKnowledgeMap.put(category, new ArrayList<>()); + } + } +} diff --git a/src/prog/ex15/exercise/i18ncountries/KnowledgeGenerator.java b/src/prog/ex15/exercise/i18ncountries/KnowledgeGenerator.java new file mode 100644 index 0000000..7f0750b --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/KnowledgeGenerator.java @@ -0,0 +1,11 @@ +package prog.ex15.exercise.i18ncountries; + +/** + * Fills a CountryKnowledgeContainer. + */ +public interface KnowledgeGenerator { + /** + * Fills knowledge into the container. + */ + void fillContainer(CountryKnowledgeContainer container); +} diff --git a/src/prog/ex15/exercise/i18ncountries/TypicalCountry.java b/src/prog/ex15/exercise/i18ncountries/TypicalCountry.java new file mode 100644 index 0000000..3db47ad --- /dev/null +++ b/src/prog/ex15/exercise/i18ncountries/TypicalCountry.java @@ -0,0 +1,46 @@ +package prog.ex15.exercise.i18ncountries; + +import java.time.LocalDate; + +/** + * Interface to make the creation of a ListResourceBundle more reliable. + */ +public interface TypicalCountry { + String VELOCITY = "velocity"; + String VELOCITY_UNIT = "velocity-unit"; + String POPULATION = "population"; + String MOST_IMPORTANT_HOLIDAY_DATE = "most-important-holiday-date"; + String MOST_IMPORTANT_HOLIDAY_NAME = "most-important-holiday-name"; + String MOST_FAMOUS_MEAL = "most-famous-meal"; + + /** + * Setter for the maximum velocity on streets. + * + * @param velocity maximum allowed speed. If there is no maximum, the recommended velocity + * should be used. + * @param unit unit for the velocity, e.g. "km/h" in Europe, "mph" in USA + */ + void setVelocity(int velocity, String unit); + + /** + * Number of people living in this country. + * + * @param population number of people + */ + void setPopulation(int population); + + /** + * Most famous meal the country is known for. + * + * @param mostFamousMeal Name of the most famous meal + */ + void setMostFamousMeal(String mostFamousMeal); + + /** + * Most important holiday in this country. + * + * @param date date of the current year the holiday takes place + * @param holidayName Name of the holiday + */ + void setMostImportantHoliday(LocalDate date, String holidayName); +} diff --git a/src/prog/ex15/monolingual/StupidKnowledgeGenerator.java b/src/prog/ex15/monolingual/StupidKnowledgeGenerator.java new file mode 100644 index 0000000..bb13050 --- /dev/null +++ b/src/prog/ex15/monolingual/StupidKnowledgeGenerator.java @@ -0,0 +1,24 @@ +package prog.ex15.monolingual; + + +import prog.ex15.exercise.i18ncountries.Category; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; +import prog.ex15.exercise.i18ncountries.KnowledgeGenerator; + +/** + * Fills knowledge into a CountryKnowledgeContainer. I18N? Never heard. + */ +public class StupidKnowledgeGenerator implements KnowledgeGenerator { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(StupidKnowledgeGenerator.class); + + @Override + public void fillContainer(CountryKnowledgeContainer container) { + container.clear(); + container.addKnowledge(Category.TRAFFIC, "Maximum speed on highways is 70 mph."); + container.addKnowledge(Category.FOOD, "Our most prominent food is Fish and Chips."); + container.addKnowledge(Category.HOLIDAYS, + "Our most important holiday is Brexit Day (Joke) on January, the 1, 2022."); + container.addKnowledge(Category.STATISTICS, "Our population is 66.500.000"); + } +} diff --git a/src/prog/ex15/monolingual/gui/FxKnowledgePresenter.java b/src/prog/ex15/monolingual/gui/FxKnowledgePresenter.java new file mode 100644 index 0000000..747373f --- /dev/null +++ b/src/prog/ex15/monolingual/gui/FxKnowledgePresenter.java @@ -0,0 +1,40 @@ +package prog.ex15.monolingual.gui; + +import java.util.List; +import javafx.scene.control.Accordion; +import javafx.scene.control.Label; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.VBox; +import prog.ex15.exercise.i18ncountries.Category; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; + +/** + * JavaFX component presenting the content of a CountryKnowledgeContainer. + */ +public class FxKnowledgePresenter extends Accordion { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(FxKnowledgePresenter.class); + + CountryKnowledgeContainer countryKnowledgeContainer; + + public FxKnowledgePresenter(final CountryKnowledgeContainer countryKnowledgeContainer) { + this.countryKnowledgeContainer = countryKnowledgeContainer; + fillAccordion(); + } + + private void fillAccordion() { + this.getPanes().clear(); + for (Category category : Category.values()) { + TitledPane titledPane = new TitledPane(); + titledPane.setText(category.toString()); + List knowledgeList = countryKnowledgeContainer.getKnowledge(category); + VBox box = new VBox(); + for (String string : knowledgeList) { + box.getChildren().add(new Label(string)); + logger.info("Adding label " + string); + } + titledPane.setContent(box); + this.getPanes().add(titledPane); + } + } +} diff --git a/src/prog/ex15/monolingual/gui/MonolingualWelcomeLauncher.java b/src/prog/ex15/monolingual/gui/MonolingualWelcomeLauncher.java new file mode 100644 index 0000000..8264b91 --- /dev/null +++ b/src/prog/ex15/monolingual/gui/MonolingualWelcomeLauncher.java @@ -0,0 +1,26 @@ +package prog.ex15.monolingual.gui; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.stage.Stage; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; +import prog.ex15.monolingual.StupidKnowledgeGenerator; + +/** + * Main to launch the WelcomeToMyCountry content in a separate application. + */ +public class MonolingualWelcomeLauncher extends Application { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(MonolingualWelcomeLauncher.class); + + @Override + public void start(final Stage stage) throws Exception { + logger.debug("start: {}", stage); + CountryKnowledgeContainer container = new CountryKnowledgeContainer(); + StupidKnowledgeGenerator generator = new StupidKnowledgeGenerator(); + generator.fillContainer(container); + FxKnowledgePresenter presenter = new FxKnowledgePresenter(container); + stage.setScene(new Scene(presenter, 400, 300)); + stage.show(); + } +} diff --git a/src/prog/ex15/solution/i18ncountries/I18nKnowledgeGenerator.java b/src/prog/ex15/solution/i18ncountries/I18nKnowledgeGenerator.java new file mode 100644 index 0000000..fb2e742 --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/I18nKnowledgeGenerator.java @@ -0,0 +1,73 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.Category; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; +import prog.ex15.exercise.i18ncountries.KnowledgeGenerator; +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.FormatStyle; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * Simple, straight-forward implementation of the KnowledgeGenerator interface for multiple + * countries. + */ +public class I18nKnowledgeGenerator implements KnowledgeGenerator { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(I18nKnowledgeGenerator.class); + + @Override + public void fillContainer(CountryKnowledgeContainer container) { + ResourceBundle resourceBundle = SingletonConfiguration.getInstance().getMessageBundle(); + container.clear(); + SingletonConfiguration config = SingletonConfiguration.getInstance(); + ResourceBundle msg = config.getMessageBundle(); + ResourceBundle data = config.getTypicalBundle(); + Locale locale = config.getLocale(); + + int velocity = (Integer) data.getObject(TypicalCountry.VELOCITY); + String unit = (String) data.getObject(TypicalCountry.VELOCITY_UNIT); + int population = (Integer) data.getObject(TypicalCountry.POPULATION); + LocalDate holidayDate = (LocalDate) data.getObject(TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE); + String holidayName = (String) data.getObject(TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME); + String meal = (String) data.getObject(TypicalCountry.MOST_FAMOUS_MEAL); + + DateTimeFormatter dateFormatter = DateTimeFormatter + .ofLocalizedDate(FormatStyle.FULL) + .withLocale(locale); + + NumberFormat numberFormatter = NumberFormat.getNumberInstance(locale); + + String trafficText = MessageFormat.format( + msg.getString("traffic.maximum.speed.highways"), + velocity, + unit + ); + container.addKnowledge(Category.TRAFFIC, trafficText); + + String foodText = MessageFormat.format( + msg.getString("food.most.prominent.food"), + meal + ); + container.addKnowledge(Category.FOOD, foodText); + + String holidayText = MessageFormat.format( + msg.getString("holiday.most.important.holiday"), + holidayName, + dateFormatter.format(holidayDate) + ); + container.addKnowledge(Category.HOLIDAYS, holidayText); + + String statisticsText = MessageFormat.format( + msg.getString("statistics.population"), + numberFormatter.format(population) + ); + container.addKnowledge(Category.STATISTICS, statisticsText); + } + +} diff --git a/src/prog/ex15/solution/i18ncountries/SingletonConfiguration.java b/src/prog/ex15/solution/i18ncountries/SingletonConfiguration.java new file mode 100644 index 0000000..57a1b0f --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/SingletonConfiguration.java @@ -0,0 +1,89 @@ +package prog.ex15.solution.i18ncountries; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import prog.ex15.exercise.i18ncountries.Configuration; +import prog.ex15.exercise.i18ncountries.Country; +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +/** + * Singleton-based implementation of the Configuration interface. + */ +public class SingletonConfiguration implements Configuration { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(SingletonConfiguration.class); + + private static SingletonConfiguration instance = new SingletonConfiguration(); + private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); + + private Locale locale; + private ResourceBundle resourceBundle; + private ResourceBundle typicalBundle; + + public static SingletonConfiguration getInstance() { + return instance; + } + + @Override + public Locale getLocale() { + return this.locale; + } + + @Override + public void setLocale(final Locale locale) { + Locale newLocale = Objects.requireNonNullElse(locale, Locale.UK); + + this.resourceBundle = ResourceBundle.getBundle("bundles/i18ncountries", newLocale); + this.typicalBundle = ResourceBundle.getBundle("prog.ex15.solution.i18ncountries.TypicalBundle", newLocale); + + Locale oldLocale = this.locale; + this.locale = newLocale; + + pcs.firePropertyChange("locale", oldLocale, this.locale); + } + + @Override + public ResourceBundle getTypicalBundle() { + return this.typicalBundle; + } + + @Override + public ResourceBundle getMessageBundle() { + return this.resourceBundle; + } + + @Override + public Map getCountry2LocaleMap() { + + return Arrays.stream(Country.values()) + .collect(Collectors.toMap( + country -> country, + country -> switch (country) { + case GERMANY -> Locale.GERMANY; + case ENGLAND -> Locale.UK; + case NETHERLANDS -> new Locale("nl", "NL"); + case DENMARK -> new Locale("da", "DK"); + } + )); + } + + @Override + public void addPropertyChangeListener(final PropertyChangeListener listener) { + if(listener == null) { + throw new NullPointerException("This Listener is null!"); + } + pcs.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(final PropertyChangeListener listener) { + if(listener == null) { + throw new NullPointerException("This Listener is null!"); + } + pcs.removePropertyChangeListener(listener); + } +} diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundle.java b/src/prog/ex15/solution/i18ncountries/TypicalBundle.java new file mode 100644 index 0000000..a4342e2 --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundle.java @@ -0,0 +1,21 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; +import java.util.ListResourceBundle; + +public class TypicalBundle extends ListResourceBundle { + + @Override + protected Object[][] getContents() { + return new Object[][] { + {TypicalCountry.VELOCITY, 70}, + {TypicalCountry.VELOCITY_UNIT, "mph"}, + {TypicalCountry.POPULATION, 69300000}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE, LocalDate.of(2026, 1, 31)}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME, "Brexit Day (Joke)"}, + {TypicalCountry.MOST_FAMOUS_MEAL, "fish and chips"} + }; + } +} diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundleBase.java b/src/prog/ex15/solution/i18ncountries/TypicalBundleBase.java new file mode 100644 index 0000000..5ace5a9 --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundleBase.java @@ -0,0 +1,49 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; + +public abstract class TypicalBundleBase implements TypicalCountry { + + protected int velocity; + protected String velocityUnit; + protected int population; + protected LocalDate holidayDate; + protected String holidayName; + protected String mostFamousMeal; + + @Override + public void setVelocity(int velocity, String unit) { + this.velocity = velocity; + this.velocityUnit = unit; + } + + @Override + public void setPopulation(int population) { + this.population = population; + } + + @Override + public void setMostFamousMeal(String mostFamousMeal) { + this.mostFamousMeal = mostFamousMeal; + } + + @Override + public void setMostImportantHoliday(LocalDate date, String holidayName) { + this.holidayDate = date; + this.holidayName = holidayName; + } + + public Object[][] contents() { + return new Object[][] { + {VELOCITY, velocity}, + {VELOCITY_UNIT, velocityUnit}, + {POPULATION, population}, + {MOST_IMPORTANT_HOLIDAY_DATE, holidayDate}, + {MOST_IMPORTANT_HOLIDAY_NAME, holidayName}, + {MOST_FAMOUS_MEAL, mostFamousMeal} + }; + } +} + diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundle_da_DK.java b/src/prog/ex15/solution/i18ncountries/TypicalBundle_da_DK.java new file mode 100644 index 0000000..e0378f1 --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundle_da_DK.java @@ -0,0 +1,21 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; +import java.util.ListResourceBundle; + +public class TypicalBundle_da_DK extends ListResourceBundle { + + @Override + protected Object[][] getContents() { + return new Object[][] { + {TypicalCountry.VELOCITY, 130}, + {TypicalCountry.VELOCITY_UNIT, "km/h"}, + {TypicalCountry.POPULATION, 6000000}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE, LocalDate.of(2026, 6, 5)}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME, "Grundlovsdag"}, + {TypicalCountry.MOST_FAMOUS_MEAL, "knækbrød"} + }; + } +} \ No newline at end of file diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundle_de_DE.java b/src/prog/ex15/solution/i18ncountries/TypicalBundle_de_DE.java new file mode 100644 index 0000000..129c51d --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundle_de_DE.java @@ -0,0 +1,21 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; +import java.util.ListResourceBundle; + +public class TypicalBundle_de_DE extends ListResourceBundle { + + @Override + protected Object[][] getContents() { + return new Object[][] { + {TypicalCountry.VELOCITY, 130}, + {TypicalCountry.VELOCITY_UNIT, "km/h"}, + {TypicalCountry.POPULATION, 83500000}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE, LocalDate.of(2026, 10, 3)}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME, "Tag der Deutschen Einheit"}, + {TypicalCountry.MOST_FAMOUS_MEAL, "Eisbein mit Sauerkraut"} + }; + } +} diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundle_en_GB.java b/src/prog/ex15/solution/i18ncountries/TypicalBundle_en_GB.java new file mode 100644 index 0000000..2c8985b --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundle_en_GB.java @@ -0,0 +1,21 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; +import java.util.ListResourceBundle; + +public class TypicalBundle_en_GB extends ListResourceBundle { + + @Override + protected Object[][] getContents() { + return new Object[][] { + {TypicalCountry.VELOCITY, 70}, + {TypicalCountry.VELOCITY_UNIT, "mph"}, + {TypicalCountry.POPULATION, 69300000}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE, LocalDate.of(2026, 1, 31)}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME, "Brexit Day (Joke)"}, + {TypicalCountry.MOST_FAMOUS_MEAL, "fish and chips"} + }; + } +} diff --git a/src/prog/ex15/solution/i18ncountries/TypicalBundle_nl_NL.java b/src/prog/ex15/solution/i18ncountries/TypicalBundle_nl_NL.java new file mode 100644 index 0000000..2fc3b2d --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/TypicalBundle_nl_NL.java @@ -0,0 +1,21 @@ +package prog.ex15.solution.i18ncountries; + +import prog.ex15.exercise.i18ncountries.TypicalCountry; + +import java.time.LocalDate; +import java.util.ListResourceBundle; + +public class TypicalBundle_nl_NL extends ListResourceBundle { + + @Override + protected Object[][] getContents() { + return new Object[][] { + {TypicalCountry.VELOCITY, 120}, + {TypicalCountry.VELOCITY_UNIT, "km/h"}, + {TypicalCountry.POPULATION, 18000000}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_DATE, LocalDate.of(2026, 4, 27)}, + {TypicalCountry.MOST_IMPORTANT_HOLIDAY_NAME, "Koningsdag"}, + {TypicalCountry.MOST_FAMOUS_MEAL, "pannenkoken"} + }; + } +} diff --git a/src/prog/ex15/solution/i18ncountries/gui/FxKnowledgePresenter.java b/src/prog/ex15/solution/i18ncountries/gui/FxKnowledgePresenter.java new file mode 100644 index 0000000..f8b823a --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/gui/FxKnowledgePresenter.java @@ -0,0 +1,48 @@ +package prog.ex15.solution.i18ncountries.gui; + +import javafx.scene.control.Accordion; +import javafx.scene.control.Label; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.VBox; +import prog.ex15.exercise.i18ncountries.Category; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; +import prog.ex15.solution.i18ncountries.SingletonConfiguration; + +import java.util.List; +import java.util.ResourceBundle; + +/** + * JavaFX component presenting the content of a CountryKnowledgeContainer. + */ +public class FxKnowledgePresenter extends Accordion { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(FxKnowledgePresenter.class); + + CountryKnowledgeContainer countryKnowledgeContainer; + + public FxKnowledgePresenter(final CountryKnowledgeContainer countryKnowledgeContainer) { + this.countryKnowledgeContainer = countryKnowledgeContainer; + fillAccordion(); + } + + private void fillAccordion() { + this.getPanes().clear(); + for (Category category : Category.values()) { + ResourceBundle messageBundle = SingletonConfiguration.getInstance().getMessageBundle(); + TitledPane titledPane = new TitledPane(); + titledPane.setText(messageBundle.getString("categories."+category.name())); + List knowledgeList = countryKnowledgeContainer.getKnowledge(category); + VBox box = new VBox(); + for (String string : knowledgeList) { + box.getChildren().add(new Label(string)); + logger.info("Adding label " + string); + } + titledPane.setContent(box); + this.getPanes().add(titledPane); + } + } + + public void refresh() { + fillAccordion(); + } +} diff --git a/src/prog/ex15/solution/i18ncountries/gui/MultilingualWelcomeLauncher.java b/src/prog/ex15/solution/i18ncountries/gui/MultilingualWelcomeLauncher.java new file mode 100644 index 0000000..3e5b521 --- /dev/null +++ b/src/prog/ex15/solution/i18ncountries/gui/MultilingualWelcomeLauncher.java @@ -0,0 +1,59 @@ +package prog.ex15.solution.i18ncountries.gui; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.ComboBox; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import prog.ex15.exercise.i18ncountries.Country; +import prog.ex15.exercise.i18ncountries.CountryKnowledgeContainer; +import prog.ex15.solution.i18ncountries.I18nKnowledgeGenerator; +import prog.ex15.solution.i18ncountries.SingletonConfiguration; + +import java.util.Locale; + +/** + * Main to launch the WelcomeToMyCountry content in a separate application. + */ +public class MultilingualWelcomeLauncher extends Application { + private static final org.slf4j.Logger logger = + org.slf4j.LoggerFactory.getLogger(MultilingualWelcomeLauncher.class); + + @Override + public void start(final Stage stage) throws Exception { + logger.debug("start: {}", stage); + + SingletonConfiguration.getInstance().setLocale(new Locale("en", "GB")); + CountryKnowledgeContainer container = new CountryKnowledgeContainer(); + I18nKnowledgeGenerator generator = new I18nKnowledgeGenerator(); + generator.fillContainer(container); + FxKnowledgePresenter presenter = new FxKnowledgePresenter(container); + + ComboBox countrySelector = new ComboBox<>(); + countrySelector.getItems().addAll(Country.values()); + countrySelector.setValue(Country.ENGLAND); + + countrySelector.setOnAction(e -> { + Country selectedCountry = countrySelector.getValue(); + Locale newLocale = SingletonConfiguration.getInstance() + .getCountry2LocaleMap() + .get(selectedCountry); + SingletonConfiguration.getInstance().setLocale(newLocale); + generator.fillContainer(container); + }); + + SingletonConfiguration.getInstance().addPropertyChangeListener(evt -> { + if ("locale".equals(evt.getPropertyName())) { + generator.fillContainer(container); + presenter.refresh(); + } + }); + + VBox root = new VBox(10); + root.getChildren().addAll(countrySelector, presenter); + + + stage.setScene(new Scene(root, 400, 300)); + stage.show(); + } +} diff --git a/testsrc/prog/ex15/welcome/Readme.md b/testsrc/prog/ex15/welcome/Readme.md new file mode 100644 index 0000000..5dd389e --- /dev/null +++ b/testsrc/prog/ex15/welcome/Readme.md @@ -0,0 +1 @@ +In this directory you as a student can write your own tests. These tests will *not* be executed on the Artemis platform. \ No newline at end of file