commit 07084bd18e77a90affcbfcd10e22364ce5d7a068 Author: Andreas Billmann Date: Sat Nov 28 23:06:14 2015 +0100 Initial commit diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4d4ef67 --- /dev/null +++ b/build.gradle @@ -0,0 +1,27 @@ +group 'ninja.javafx' +version '1.0-SNAPSHOT' + +apply plugin: 'java' +apply plugin: 'groovy' +apply plugin:'application' +apply plugin: 'idea' + +sourceCompatibility = 1.8 +mainClassName = 'ninja.javafx.smartcsv.fx.SmartCSV' + +repositories { + mavenCentral() + jcenter() +} + +dependencies { + testCompile group: 'junit', name: 'junit', version: '4.12' + testCompile group: 'org.testfx', name: 'testfx-core', version: '4.0.+' + testCompile group: 'org.testfx', name: 'testfx-junit', version: '4.0.+' + testCompile group: 'org.mockito', name: 'mockito-all', version: '1.10.19' + compile group: 'org.codehaus.groovy', name: 'groovy-all', version: '2.4.5' + compile group: 'org.springframework', name:'spring-context', version: '4.2.3.RELEASE' + compile group: 'net.sf.supercsv', name: 'super-csv', version: '2.4.0' + compile group: 'com.typesafe', name: 'config', version: '1.3.0' + compile group: 'commons-validator', name: 'commons-validator', version: '1.4.1' +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..2322723 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..018cbda --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Oct 01 19:02:27 CEST 2015 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-all.zip diff --git a/gradlew b/gradlew new file mode 100644 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +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" + which java >/dev/null 2>&1 || 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 + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..8a0b282 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@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 + +@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= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +: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 %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="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! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/license b/license new file mode 100644 index 0000000..a57e612 --- /dev/null +++ b/license @@ -0,0 +1,28 @@ +The MIT License (MIT) +------------------------------------------------------------------------------------------------------------------------ + +Copyright (c) 2015 Andreas Billmann + + + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + + + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + + + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..0d0cf9a --- /dev/null +++ b/settings.gradle @@ -0,0 +1,2 @@ +rootProject.name = 'SmartCSV.ninja.javafx.smartcsv.fx' + diff --git a/src/main/java/ninja/javafx/smartcsv/FileReader.java b/src/main/java/ninja/javafx/smartcsv/FileReader.java new file mode 100644 index 0000000..43ab82a --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/FileReader.java @@ -0,0 +1,37 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv; + +import java.io.File; +import java.io.IOException; + +/** + * Created by Andreas on 19.11.2015. + */ +public interface FileReader { + void read(File filename) throws IOException; +} diff --git a/src/main/java/ninja/javafx/smartcsv/csv/CSVFileReader.java b/src/main/java/ninja/javafx/smartcsv/csv/CSVFileReader.java new file mode 100644 index 0000000..c4dbb0d --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/csv/CSVFileReader.java @@ -0,0 +1,80 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.csv; + +import ninja.javafx.smartcsv.FileReader; +import ninja.javafx.smartcsv.fx.table.model.CSVModel; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import org.springframework.stereotype.Service; +import org.supercsv.io.CsvMapReader; +import org.supercsv.io.ICsvMapReader; +import org.supercsv.prefs.CsvPreference; + +import java.io.File; +import java.io.IOException; +import java.util.Map; + +/** + * Created by Andreas on 18.11.2015. + */ +@Service +public class CSVFileReader implements FileReader { + + private CSVModel model; + + @Override + public void read(File file) throws IOException { + + ICsvMapReader mapReader = null; + try { + mapReader = new CsvMapReader(new java.io.FileReader(file.getAbsoluteFile()), CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE); + model = new CSVModel(file.getAbsolutePath()); + + // the header columns are used as the keys to the Map + String[] header = mapReader.getHeader(true); + model.setHeader(header); + + Map customerMap; + while ((customerMap = mapReader.read(header)) != null) { + CSVRow row = model.addRow(); + for (String column : header) { + row.addValue(column, customerMap.get(column)); + } + } + + } finally { + if (mapReader != null) { + mapReader.close(); + } + } + } + + public CSVModel getData() { + return model; + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/csv/CSVFileWriter.java b/src/main/java/ninja/javafx/smartcsv/csv/CSVFileWriter.java new file mode 100644 index 0000000..7b6e097 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/csv/CSVFileWriter.java @@ -0,0 +1,80 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.csv; + +import ninja.javafx.smartcsv.fx.table.model.CSVModel; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import org.springframework.stereotype.Service; +import org.supercsv.io.CsvMapWriter; +import org.supercsv.io.ICsvMapWriter; +import org.supercsv.prefs.CsvPreference; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.Map; + +import static java.util.stream.Collectors.toMap; + +/** + * filewriter for the csv + */ +@Service +public class CSVFileWriter { + + public void saveFile(CSVModel model) throws IOException { + ICsvMapWriter mapWriter = null; + try { + mapWriter = new CsvMapWriter(new FileWriter(model.getFilepath()), CsvPreference.EXCEL_NORTH_EUROPE_PREFERENCE); + mapWriter.writeHeader(model.getHeader()); + + for(CSVRow row: model.getRows()) { + Map columns = convertMapFromModel(row); + mapWriter.write(columns, model.getHeader()); + } + } + finally { + if( mapWriter != null ) { + mapWriter.close(); + } + } + } + + /** + * transforms the column map from CSVValue to a simple Map + * @param row the row to convert + * @return a simple map for the supercvs writer + */ + private Map convertMapFromModel(CSVRow row) { + return row.getColumns().entrySet().stream() + .collect( + toMap( + Map.Entry::getKey, + e -> e.getValue().getValue().getValue() != null ? e.getValue().getValue().getValue() : "" + ) + ); + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/FXMLController.java b/src/main/java/ninja/javafx/smartcsv/fx/FXMLController.java new file mode 100644 index 0000000..cad8fb9 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/FXMLController.java @@ -0,0 +1,72 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx; + + +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ResourceBundle; + +public abstract class FXMLController implements InitializingBean, Initializable { + + protected Node view; + protected String fxmlFilePath; + protected String resourcePath; + + public abstract void setFxmlFilePath(String filePath); + + @Value("${resource.main}") + public void setResourceBundle(String resourcePath) { + this.resourcePath = resourcePath; + } + + @Override + public void afterPropertiesSet() throws Exception { + loadFXML(); + } + + protected final void loadFXML() throws IOException { + try (InputStream fxmlStream = getClass().getResourceAsStream(fxmlFilePath)) { + FXMLLoader loader = new FXMLLoader(); + loader.setResources(ResourceBundle.getBundle(this.resourcePath)); + loader.setController(this); + this.view = (loader.load(fxmlStream)); + } catch (Throwable t) { + t.printStackTrace(); + } + } + + public Node getView() { + return view; + } +} \ No newline at end of file diff --git a/src/main/java/ninja/javafx/smartcsv/fx/SmartCSV.java b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSV.java new file mode 100644 index 0000000..2907e20 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSV.java @@ -0,0 +1,84 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx; + +import javafx.application.Application; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; +import org.springframework.context.annotation.*; +import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; + +@Configuration +@ComponentScan("ninja.javafx") +@PropertySource(value = "classpath:/ninja/javafx/smartcsv/fx/application.properties") +public class SmartCSV extends Application { + + private AnnotationConfigApplicationContext appContext; + + @Override + public void start(Stage primaryStage) throws Exception { + appContext = new AnnotationConfigApplicationContext(SmartCSV.class); + String name = appContext.getEnvironment().getProperty("application.name"); + String version = appContext.getEnvironment().getProperty("application.version"); + + try { + showUI(primaryStage, name, version); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Bean + public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() { + return new PropertySourcesPlaceholderConfigurer(); + } + + @Override + public void stop() throws Exception { + if (appContext != null) { + appContext.close(); + } + + super.stop(); + } + + public static void main(String[] args) { + launch(args); + } + + private void showUI(Stage primaryStage, String name, String version) { + SmartCSVController smartCVSController = appContext.getBean(SmartCSVController.class); + Scene scene = new Scene((Parent) smartCVSController.getView()); + + primaryStage.setScene(scene); + primaryStage.setTitle(String.format("%s %s", name, version)); + primaryStage.show(); + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java new file mode 100644 index 0000000..2f8c74c --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/SmartCSVController.java @@ -0,0 +1,290 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx; + +import javafx.application.Platform; +import javafx.concurrent.Service; +import javafx.concurrent.Task; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.Label; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.layout.BorderPane; +import javafx.stage.FileChooser; +import ninja.javafx.smartcsv.FileReader; +import ninja.javafx.smartcsv.csv.CSVFileReader; +import ninja.javafx.smartcsv.csv.CSVFileWriter; +import ninja.javafx.smartcsv.fx.table.ObservableMapValueFactory; +import ninja.javafx.smartcsv.fx.table.ValidationCellFactory; +import ninja.javafx.smartcsv.fx.table.model.CSVModel; +import ninja.javafx.smartcsv.fx.table.model.CSVValue; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import ninja.javafx.smartcsv.validation.ValidationFileReader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +import static javafx.application.Platform.runLater; + +/** + * main controller of the application + */ +@Component +public class SmartCSVController extends FXMLController { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // injections + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Autowired + private CSVFileReader csvLoader; + + @Autowired + private ValidationFileReader validationLoader; + + @Autowired + private CSVFileWriter csvFileWriter; + + @FXML + private BorderPane applicationPane; + + @FXML + private Label stateline; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // injections + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private ValidationCellFactory cellFactory = new ValidationCellFactory(); + private final LoadCSVService loadCSVService = new LoadCSVService(); + private final SaveCSVService saveCSVService = new SaveCSVService(); + private CSVModel model; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // init + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Override + public void initialize(URL location, ResourceBundle resources) { + stateline.setVisible(false); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // setter + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @Value("${fxml.smartcvs.view}") + @Override + public void setFxmlFilePath(String filePath) { + this.fxmlFilePath = filePath; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // actions + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + @FXML + public void openCsv(ActionEvent actionEvent) { + loadFile(csvLoader, "CSV files (*.csv)", "*.csv", "Open CSV"); + } + + @FXML + public void openConfig(ActionEvent actionEvent) { + loadFile(validationLoader, "JSON files (*.json)", "*.json", "Open Validation Configuration"); + } + + @FXML + public void saveCsv(ActionEvent actionEvent) { + saveCSVService.restart(); + } + + @FXML + public void saveAsCsv(ActionEvent actionEvent) { + saveFile(csvFileWriter, "CSV files (*.csv)", "*.csv"); + } + + @FXML + public void close(ActionEvent actionEvent) { + Platform.exit(); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // private methods + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void loadFile(FileReader fileReader, String filterText, String filter, String title) { + final FileChooser fileChooser = new FileChooser(); + + //Set extension filter + final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(filterText, filter); + fileChooser.getExtensionFilters().add(extFilter); + fileChooser.setTitle(title); + + //Show open file dialog + final File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow()); + if (file != null) { + loadCSVService.setFile(file); + loadCSVService.setFileReader(fileReader); + loadCSVService.restart(); + } + } + + private void saveFile(CSVFileWriter writer, String filterText, String filter) { + if (model != null) { + final FileChooser fileChooser = new FileChooser(); + + //Set extension filter + final FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(filterText, filter); + fileChooser.getExtensionFilters().add(extFilter); + + File initfile = new File(model.getFilepath()); + fileChooser.setInitialDirectory(initfile.getParentFile()); + fileChooser.setInitialFileName(initfile.getName()); + fileChooser.setTitle("Save File"); + + //Show open file dialog + final File file = fileChooser.showOpenDialog(applicationPane.getScene().getWindow()); + if (file != null) { + model.setFilepath(file.getAbsolutePath()); + saveCSVService.restart(); + } + } + } + + /** + * Creates new table view and add the new content + */ + private void resetContent() { + model = csvLoader.getData(); + model.setValidator(validationLoader.getValidator()); + + TableView tableView = new TableView<>(); + + for (String column: model.getHeader()) { + addColumn(column, tableView); + } + tableView.getItems().setAll(model.getRows()); + tableView.setEditable(true); + + applicationPane.setCenter(tableView); + } + + /** + * Adds a column with the given name to the tableview + * @param header name of the column header + * @param tableView the tableview + */ + private void addColumn(String header, TableView tableView) { + TableColumn column = new TableColumn(header); + column.setCellValueFactory(new ObservableMapValueFactory(header)); + column.setCellFactory(cellFactory); + column.setEditable(true); + column.setOnEditCommit(new EventHandler>() { + @Override + public void handle(TableColumn.CellEditEvent event) { + event.getTableView().getItems().get(event.getTablePosition().getRow()). + getColumns().get(header). + setValue(event.getNewValue()); + } + }); + + tableView.getColumns().add(column); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // inner class + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Service class for async load of a csv file + */ + private class LoadCSVService extends Service { + + private File file = null; + private FileReader fileReader; + + public void setFile(File value) { + file = value; + } + public void setFileReader(FileReader fileReader) { + this.fileReader = fileReader; + } + + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + if (file != null) { + try { + fileReader.read(file); + runLater(SmartCSVController.this::resetContent); + } catch (IOException ex) { + ex.printStackTrace(); + } + } + return null; + } + }; + } + } + + /** + * Service class for async load of a csv file + */ + private class SaveCSVService extends Service { + + @Override + protected Task createTask() { + return new Task() { + @Override + protected Void call() throws Exception { + try { + csvFileWriter.saveFile(model); + runLater(SmartCSVController.this::resetContent); + } catch (IOException ex) { + ex.printStackTrace(); + } + return null; + } + }; + } + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java b/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java new file mode 100644 index 0000000..9576417 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/EditableValidationCell.java @@ -0,0 +1,136 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table; + +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.TableCell; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.input.KeyCode; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import ninja.javafx.smartcsv.fx.table.model.CSVValue; + +import static javafx.application.Platform.runLater; + +/** + * Created by Andreas on 27.11.2015. + */ +public class EditableValidationCell extends TableCell { + + private ValueTextField textField; + + @Override + public void startEdit() { + super.startEdit(); + setTextField(); + runLater(() -> { + textField.requestFocus(); + textField.selectAll(); + }); + } + + @Override + public void cancelEdit() { + super.cancelEdit(); + setText(getItem().getValue()); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + + @Override + protected void updateItem(CSVValue item, boolean empty) { + super.updateItem(item, empty); + + if (item == null || item.getValid().isValid() || isEditing()) { + setStyle(""); + setTooltip(null); + } else if (!item.getValid().isValid()) { + setStyle("-fx-background-color: #ff8888"); + setTooltip(new Tooltip(item.getValid().error())); + } + + if (item == null || empty) { + setTextInCell(null); + } else { + if (isEditing()) { + setTextField(); + textField.setValue(item); + } else { + setTextInCell(item.getValue()); + } + } + } + + private void setTextField() { + if (textField == null) { + createTextField(); + } + setGraphic(textField); + setContentDisplay(ContentDisplay.GRAPHIC_ONLY); + } + + private void setTextInCell(String text) { + setGraphic(null); + setText(text); + setContentDisplay(ContentDisplay.TEXT_ONLY); + } + + private void createTextField() { + textField = new ValueTextField(getItem()); + textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2); + textField.setOnKeyPressed(t -> { + if (t.getCode() == KeyCode.ENTER) { + commitEdit(textField.getValue()); + } else if (t.getCode() == KeyCode.ESCAPE) { + cancelEdit(); + } + }); + textField.focusedProperty().addListener((observable, oldValue, newValue) -> { + if (!newValue && textField != null) { + commitEdit(textField.getValue()); + } + }); + } + + private class ValueTextField extends TextField { + private CSVValue value; + + public ValueTextField(CSVValue value) { + setValue(value); + } + + public void setValue(CSVValue value) { + this.value = value; + setText(value.getValue()); + } + + public CSVValue getValue() { + value.setValue(getText()); + return value; + } + + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/ObservableMapValueFactory.java b/src/main/java/ninja/javafx/smartcsv/fx/table/ObservableMapValueFactory.java new file mode 100644 index 0000000..2bacdba --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/ObservableMapValueFactory.java @@ -0,0 +1,53 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table; + +import javafx.beans.property.ObjectProperty; +import javafx.scene.control.TableColumn; +import javafx.util.Callback; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import ninja.javafx.smartcsv.fx.table.model.CSVValue; + +/** + * Created by Andreas on 18.11.2015. + */ +public class ObservableMapValueFactory implements + Callback, ObjectProperty> { + + private final Object key; + + public ObservableMapValueFactory(Object key) { + this.key = key; + } + + @Override + public ObjectProperty call(TableColumn.CellDataFeatures features) { + CSVRow row = features.getValue(); + ObjectProperty value = row.getColumns().get(key); + return value; + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java b/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java new file mode 100644 index 0000000..3e77d69 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/ValidationCellFactory.java @@ -0,0 +1,45 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table; + +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.util.Callback; +import ninja.javafx.smartcsv.fx.table.model.CSVModel; +import ninja.javafx.smartcsv.fx.table.model.CSVRow; +import ninja.javafx.smartcsv.fx.table.model.CSVValue; + +/** + * Created by Andreas on 18.11.2015. + */ +public class ValidationCellFactory implements Callback, TableCell> { + + @Override + public TableCell call(TableColumn param) { + return new EditableValidationCell(); + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVModel.java b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVModel.java new file mode 100644 index 0000000..7a2f31e --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVModel.java @@ -0,0 +1,121 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table.model; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import ninja.javafx.smartcsv.validation.ValidationState; +import ninja.javafx.smartcsv.validation.Validator; + +/** + * The CSVModel is the client representation for the csv filepath. + * It holds the data in rows, stores the header and manages the validator. + */ +public class CSVModel { + + private Validator validator; + private ObservableList rows = FXCollections.observableArrayList(); + private String[] header; + private String filepath; + + public CSVModel(String filepath) { + this.filepath = filepath; + } + + public String getFilepath() { + return this.filepath; + } + + public void setFilepath(String filepath) { + this.filepath = filepath; + } + + /** + * sets the validator for the data revalidates + * @param validator the validator for this data + */ + public void setValidator(Validator validator) { + this.validator = validator; + revalidate(); + } + + /** + * returns the data as a list of rows of the + * @return list of rows + */ + public ObservableList getRows() { + return rows; + } + + /** + * adds a new and empty row + * @return the new row + */ + public CSVRow addRow() { + CSVRow row = new CSVRow(); + row.setValidator(validator); + row.setRowNumber(rows.size()); + rows.add(row); + return row; + } + + /** + * sets the column headers as string array + * @param header the headers of the columns + */ + public void setHeader(String[] header) { + this.header = header; + } + + /** + * returns the column headers + * @return the column headers + */ + public String[] getHeader() { + return header; + } + + + /** + * walks through the data and validates each value + */ + private void revalidate() { + for (CSVRow row: rows) { + row.setValidator(validator); + for (String column: row.getColumns().keySet()) { + CSVValue value = row.getColumns().get(column).getValue(); + value.setValidator(validator); + if (validator != null) { + value.setValid(validator.isValid(column, value.getValue())); + } else { + value.setValid(new ValidationState()); + } + } + } + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVRow.java b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVRow.java new file mode 100644 index 0000000..ac2bffc --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVRow.java @@ -0,0 +1,90 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table.model; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableMap; +import ninja.javafx.smartcsv.validation.Validator; + +/** + * This class represents a single row in the csv file. + */ +public class CSVRow { + private Validator validator; + private ObservableMap> columns = FXCollections.observableHashMap(); + private int rowNumber; + + /** + * single row + * @param validator the reference to the validator + */ + public void setValidator(Validator validator) { + this.validator = validator; + } + + /** + * sets the row number + * @param rowNumber + */ + public void setRowNumber(int rowNumber) { + this.rowNumber = rowNumber; + } + + /** + * return the row number + * @return row number + */ + public int getRowNumber() { + return rowNumber; + } + + + /** + * returns the columns with data as Map + * @return columns with data + */ + public ObservableMap> getColumns() { + return columns; + } + + /** + * stores the given value in the given column of this row + * @param column column name + * @param value the value to store + */ + public void addValue(String column, String value) { + CSVValue v = new CSVValue(); + v.setValidator(validator); + v.setValue(value); + v.setColumn(column); + v.setRowNumber(rowNumber); + columns.put(column, new SimpleObjectProperty<>(v)); + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVValue.java b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVValue.java new file mode 100644 index 0000000..dce22e3 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/fx/table/model/CSVValue.java @@ -0,0 +1,112 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table.model; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import ninja.javafx.smartcsv.validation.ValidationState; +import ninja.javafx.smartcsv.validation.Validator; + +/** + * The csv value represents the value of a single cell. + * It also knows about the position (row and column) + * and if the value is valid based on the validator. + */ +public class CSVValue { + private Validator validator; + private int rowNumber; + private String column; + private StringProperty value = new SimpleStringProperty(); + private ValidationState valid = new ValidationState(); + + /** + * single value of a cell + * @param validator the reference to the validator + */ + public void setValidator(Validator validator) { + this.validator = validator; + } + + /** + * the row number this value is stored in + * @param row row number + */ + public void setRowNumber(int row) { + this.rowNumber = row; + } + + /** + * the column this value is stored in + * @param column header name of the column + */ + public void setColumn(String column) { + this.column = column; + } + + /** + * returns the real value + * @return the real value + */ + public String getValue() { + return value.get(); + } + + /** + * JavaFX property representation of the real value + * @return property of real value + */ + public StringProperty valueProperty() { + return value; + } + + /** + * sets the real value + * @param value the real value + */ + public void setValue(String value) { + if (validator != null) { + valid = validator.isValid(column, value); + } + this.value.set(value); + } + + /** + * returns if the value is valid to the rules of the validator + * @return + */ + public ValidationState getValid() { + return valid; + } + + /** + * sets the state if a value is valid or not + * @param valid the validation state + */ + protected void setValid(ValidationState valid) { + this.valid = valid; + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/validation/ValidationFileReader.java b/src/main/java/ninja/javafx/smartcsv/validation/ValidationFileReader.java new file mode 100644 index 0000000..82a29a9 --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/validation/ValidationFileReader.java @@ -0,0 +1,53 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.validation; + +import com.typesafe.config.Config; +import com.typesafe.config.ConfigFactory; +import ninja.javafx.smartcsv.FileReader; +import org.springframework.stereotype.Service; + +import java.io.File; +import java.io.IOException; + +/** + * This class loads the constraints as json config + */ +@Service +public class ValidationFileReader implements FileReader { + + private Config config; + + @Override + public void read(File file) throws IOException { + config = ConfigFactory.parseFile(file); + } + + public Validator getValidator() { + return new Validator(config); + } +} diff --git a/src/main/java/ninja/javafx/smartcsv/validation/ValidationState.java b/src/main/java/ninja/javafx/smartcsv/validation/ValidationState.java new file mode 100644 index 0000000..b0423ea --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/validation/ValidationState.java @@ -0,0 +1,51 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.validation; + +import java.io.StringWriter; + +/** + * Created by Andreas on 28.11.2015. + */ +public class ValidationState { + private boolean valid = true; + private StringWriter messages = new StringWriter(); + + public void invalidate(String message) { + valid = false; + messages.append(message).append('\n'); + } + + public boolean isValid() { + return valid; + } + + public String error() { + return messages.toString(); + } + +} diff --git a/src/main/java/ninja/javafx/smartcsv/validation/Validator.java b/src/main/java/ninja/javafx/smartcsv/validation/Validator.java new file mode 100644 index 0000000..5b5614e --- /dev/null +++ b/src/main/java/ninja/javafx/smartcsv/validation/Validator.java @@ -0,0 +1,216 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.validation; + +import com.typesafe.config.Config; +import groovy.lang.Binding; +import groovy.lang.GroovyShell; +import groovy.lang.Script; +import org.codehaus.groovy.control.CompilationFailedException; + +import java.util.HashMap; +import java.util.Map; + +import static org.apache.commons.validator.GenericValidator.*; + +/** + * This class checks all the validations defined in the + * Config against a given value + */ +public class Validator { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // member variables + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private Config validationConfig; + private GroovyShell shell = new GroovyShell(); + private Map scriptCache = new HashMap<>(); + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // constructors + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * JSON configuration for this validator + * @param validationConfig + */ + public Validator(Config validationConfig) { + this.validationConfig = validationConfig; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // oublic methods + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * checks if the value is valid for the column configuration + * @param column the column name + * @param value the value to check + * @return ValidationState with information if valid and if not which error happened + */ + public ValidationState isValid(String column, String value) { + ValidationState result = new ValidationState(); + if (validationConfig != null) { + Config columnConfig = getColumnConfig(column); + if (columnConfig != null) { + checkBlankOrNull(columnConfig, value, result); + if (value != null) { + checkRegularExpression(columnConfig, value, result); + checkAlphaNumeric(columnConfig, value, result); + checkDate(columnConfig, value, result); + checkMaxLength(columnConfig, value, result); + checkMinLength(columnConfig, value, result); + checkInteger(columnConfig, value, result); + checkGroovy(column, columnConfig, value, result); + } + } + } + return result; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // private methods + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void checkGroovy(String column, Config columnConfig, String value, ValidationState result) { + String groovyScript = getString(columnConfig, "groovy"); + if (groovyScript != null) { + + Script script = scriptCache.get(column); + if (script == null) { + script = shell.parse(groovyScript); + scriptCache.put(column, script); + } + + Binding binding = new Binding(); + binding.setVariable("value", value); + script.setBinding(binding); + + Object groovyResult = null; + try { + groovyResult = script.run(); + } catch (CompilationFailedException e) { + result.invalidate("groovy script '"+groovyScript+"' throws exception: "+e.getMessage()); + e.printStackTrace(); + } + if (groovyResult == null) { + result.invalidate("groovy script '"+groovyScript+"' returns null"); + } + + if (!isScriptResultTrue(groovyResult)) { + result.invalidate(groovyResult.toString()); + } + + } + } + + private boolean isScriptResultTrue(Object groovyResult) { + return groovyResult.equals(true) || groovyResult.toString().trim().toLowerCase().equals("true"); + } + + private void checkBlankOrNull(Config columnConfig, String value, ValidationState result) { + if (getBoolean(columnConfig, "not empty")) { + if (isBlankOrNull(value)) { + result.invalidate("should not be empty"); + } + } + } + + private void checkInteger(Config columnConfig, String value, ValidationState result) { + if (getBoolean(columnConfig, "integer")) { + if (!isInt(value)) { + result.invalidate("should be an integer"); + } + } + } + + private void checkMinLength(Config columnConfig, String value, ValidationState result) { + Integer minLength = getInteger(columnConfig, "minlength"); + if (minLength != null) { + if (!minLength(value, minLength)) { + result.invalidate("has not min length of " + minLength); + } + } + } + + private void checkMaxLength(Config columnConfig, String value, ValidationState result) { + Integer maxLength = getInteger(columnConfig, "maxlength"); + if (maxLength != null) { + if (!maxLength(value, maxLength)) { + result.invalidate("has not max length of " + maxLength); + } + } + } + + private void checkDate(Config columnConfig, String value, ValidationState result) { + String dateformat = getString(columnConfig, "date"); + if (dateformat != null && !dateformat.trim().isEmpty()) { + if (!isDate(value, dateformat, true)) { + result.invalidate("is not a date of format " + dateformat); + } + } + } + + private void checkAlphaNumeric(Config columnConfig, String value, ValidationState result) { + if (getBoolean(columnConfig, "alphanumeric")) { + if (!matchRegexp(value, "[0-9a-zA-Z]*")) { + result.invalidate("should not be alphanumeric"); + } + } + } + + private void checkRegularExpression(Config columnConfig, String value, ValidationState result) { + String regexp = getString(columnConfig, "regexp"); + if (regexp != null && !regexp.trim().isEmpty()) { + if (!matchRegexp(value, regexp)) { + result.invalidate("does not match " + regexp); + } + } + } + + private Config getColumnConfig(String column) { + return validationConfig.hasPath(column) ? validationConfig.getConfig(column) : null; + } + + private String getString(Config columnConfig, String path) { + return columnConfig.hasPath(path) ? columnConfig.getString(path) : null; + } + + private Integer getInteger(Config columnConfig, String path) { + return columnConfig.hasPath(path) ? columnConfig.getInt(path) : null; + } + + + private boolean getBoolean(Config columnConfig, String path) { + return columnConfig.hasPath(path) && columnConfig.getBoolean(path); + } + +} diff --git a/src/main/resources/ninja/javafx/smartcsv/fx/application.properties b/src/main/resources/ninja/javafx/smartcsv/fx/application.properties new file mode 100644 index 0000000..dce16ec --- /dev/null +++ b/src/main/resources/ninja/javafx/smartcsv/fx/application.properties @@ -0,0 +1,8 @@ +application.name = SmartCSV.fx +application.version = 0.1 + +# fxml views +fxml.smartcvs.view = /ninja/javafx/smartcsv/fx/smartcsv.fxml + +# resources +resource.main = ninja.javafx.smartcsv.fx.smartcsv \ No newline at end of file diff --git a/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv.css b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv.css new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv.fxml b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv.fxml new file mode 100644 index 0000000..50ff2c9 --- /dev/null +++ b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + + + + +
diff --git a/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_de.properties b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_de.properties new file mode 100644 index 0000000..abd2741 --- /dev/null +++ b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_de.properties @@ -0,0 +1,52 @@ +menu.title.import.gpx = Importiere GPX +menu.title.file = Datei +menu.title.quit = Beenden +menu.title.help = Hilfe +menu.title.settings = Einstellungen +menu.title.about = \u00dcber GeoFroggerFX +menu.title.plugins = Plugins +menu.title.list = Listen +menu.title.list.new = Liste anlegen +menu.title.list.delete = Liste l\u00F6schen + +menu.title.sort = Sortieren +menu.title.filter = Filtern + +label.text.cache.list=Liste: +label.text.cache.list.count=Anzahl: +label.text.name=Name: +label.text.difficulty=Schwierigkeit: +label.text.terrain=Gel\u00E4nde +label.text.placedBy=Platziert von: +label.text.owner=Betreut von: +label.text.date=Datum: +label.text.type=Typ: +label.text.container=Gr\u00F6\u00dfe: +label.text.shortdescription=Kurze Beschreibung: +label.text.htmldescription=HTML Beschreibung +label.text.longdescription=Lange Beschreibung: + +tab.text.descriptions=Beschreibungen +tab.text.general=Allgemein + +sort.cache.name = GC Code +sort.cache.type = Art +sort.cache.difficulty = Schwierigkeitsgrad +sort.cache.terrain = Gel\u00E4nde +sort.cache.placedBy = Platziert von +sort.cache.owner = Betreut von + +dialog.title.about = Über +dialog.title.new_list = Neue Liste +dialog.label.listname = Name der Liste: +dialog.msg.list.does.exist = Diese Liste existiert schon! +dialog.title.delete_list = Liste löschen + +all.caches = Alle Caches + +status.load.all.caches.from.db = Lade alle Caches von der Datenbank. +status.all.cache.lists.loaded = Alle Listen geladen. +status.load.caches.from.db = Lade Caches von der Datenbank. +status.all.caches.loaded = Alle Caches geladen. +status.store.all.caches = Speichere Caches in Datenbank. +status.all.caches.stored = Alle Caches in der Datenbank gespeichert. \ No newline at end of file diff --git a/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_en.properties b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_en.properties new file mode 100644 index 0000000..41802fb --- /dev/null +++ b/src/main/resources/ninja/javafx/smartcsv/fx/smartcsv_en.properties @@ -0,0 +1,53 @@ +menu.title.import.gpx = Import GPX +menu.title.file = File +menu.title.quit = Quit +menu.title.help = Help +menu.title.settings = Settings +menu.title.about = About GeoFroggerFX +menu.title.plugins = Plugins +menu.title.list = Lists +menu.title.list.new = New list +menu.title.list.delete = Delete list + + +menu.title.sort = Sort +menu.title.filter = Filter + +label.text.cache.list=List: +label.text.cache.list.count=Number: +label.text.name=Name: +label.text.difficulty=Difficulty: +label.text.terrain=Terrain: +label.text.placedBy=Placed By: +label.text.owner=Owner: +label.text.date=Date: +label.text.type=Type: +label.text.container=Container: +label.text.shortdescription=Short description: +label.text.htmldescription=HTML Description +label.text.longdescription=Long description: + +tab.text.descriptions=Descriptions +tab.text.general=General + +sort.cache.name = GC Code +sort.cache.type = Type +sort.cache.difficulty = Difficulty +sort.cache.terrain = Terrain +sort.cache.placedBy = Placed by +sort.cache.owner = Owner + +dialog.title.about = About +dialog.title.new_list = New list +dialog.label.listname = Name of list: +dialog.msg.list.does.exist = List does already exist! +dialog.title.delete_list = Delete list + +all.caches = All caches + +status.load.all.caches.from.db = Load cache lists from database. +status.all.cache.lists.loaded = All cache lists loaded. +status.load.caches.from.db = Load caches from database. +status.all.caches.loaded = All caches loaded. +status.store.all.caches = Store caches in database. +status.all.caches.stored = All caches are stored in database. \ No newline at end of file diff --git a/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVModelTest.java b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVModelTest.java new file mode 100644 index 0000000..8ff6939 --- /dev/null +++ b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVModelTest.java @@ -0,0 +1,103 @@ +/* + The MIT License (MIT) + ----------------------------------------------------------------------------- + + Copyright (c) 2015 javafx.ninja + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +package ninja.javafx.smartcsv.fx.table.model; + +import ninja.javafx.smartcsv.validation.Validator; +import org.junit.Test; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; +import static org.mockito.Mockito.*; + +/** + * unit test for the csv model + */ +public class CSVModelTest { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // constants + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static final String TESTHEADER = "TESTHEADER"; + static final String TESTVALUE = "TESTVALUE"; + static final String FILEPATH = "filepath"; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // subject under test + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + CSVModel sut = new CSVModel(FILEPATH); + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tests + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Test + public void fresh_model_has_empty_rows() { + // assertion + assertThat(sut.getRows(), empty()); + } + + @Test + public void adds_a_new_row_into_row_list() { + // execution + CSVRow newRow = sut.addRow(); + + // assertion + assertThat(sut.getRows(), contains(newRow)); + } + + @Test + public void new_row_has_last_index_of_list_as_rownumber() { + // execution + CSVRow newRow = sut.addRow(); + + // assertion + assertThat(sut.getRows().indexOf(newRow), is(newRow.getRowNumber())); + } + + @Test + public void set_validator_calls_when_model_has_data() { + + // setup + Validator validator = mock(Validator.class); + setup_model_with_one_row_one_column_and_value(); + + // execution + sut.setValidator(validator); + + // assertion + verify(validator).isValid(TESTHEADER, TESTVALUE); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // private methods + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + private void setup_model_with_one_row_one_column_and_value() { + sut.setHeader(new String[] {TESTHEADER}); + sut.addRow().addValue(TESTHEADER, TESTVALUE); + } + +} \ No newline at end of file diff --git a/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVRowTest.java b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVRowTest.java new file mode 100644 index 0000000..39230d8 --- /dev/null +++ b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVRowTest.java @@ -0,0 +1,43 @@ +package ninja.javafx.smartcsv.fx.table.model; + +import ninja.javafx.smartcsv.validation.Validator; +import org.hamcrest.Matchers; +import org.junit.Test; + +import static org.hamcrest.Matchers.hasKey; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.*; +import static org.mockito.Mockito.mock; + +/** + * unit test for row class + */ +public class CSVRowTest { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // constants + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static final String COLUMN = "COLUMN"; + static final String VALUE = "VALUE"; + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // subject under test + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + CSVRow sut = new CSVRow(); + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tests + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Test + public void adds_column_and_value_to_row() { + // execution + sut.addValue(COLUMN, VALUE); + + // assertion + assertThat(sut.getColumns(), hasKey(COLUMN)); + assertThat(sut.getColumns().get(COLUMN).get().getValue(), is(VALUE)); + } + + +} \ No newline at end of file diff --git a/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVValueTest.java b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVValueTest.java new file mode 100644 index 0000000..b2bff24 --- /dev/null +++ b/src/test/java/ninja/javafx/smartcsv/fx/table/model/CSVValueTest.java @@ -0,0 +1,58 @@ +package ninja.javafx.smartcsv.fx.table.model; + +import ninja.javafx.smartcsv.validation.Validator; +import org.junit.Before; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +/** + * unit test for the value class + */ +public class CSVValueTest { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // constants + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + static final String COLUMN = "COLUMN"; + static final String VALUE = "VALUE"; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // mocks + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + Validator validator = mock(Validator.class); + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // subject under test + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + CSVValue sut; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // init + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Before + public void initialize() { + sut = new CSVValue(); + sut.setValidator(validator); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tests + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Test + public void set_value_calls_validation() { + //setup + sut.setColumn(COLUMN); + + // execution + sut.setValue(VALUE); + + // assertion + verify(validator).isValid(COLUMN, VALUE); + } +} \ No newline at end of file diff --git a/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java b/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java new file mode 100644 index 0000000..d2ba076 --- /dev/null +++ b/src/test/java/ninja/javafx/smartcsv/validation/ValidatorTest.java @@ -0,0 +1,131 @@ +package ninja.javafx.smartcsv.validation; + +import com.typesafe.config.Config; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.Collection; + +import static java.util.Arrays.asList; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * unit test for validator + */ +@RunWith(Parameterized.class) +public class ValidatorTest { + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // parameters + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + private Config config; + private String column; + private String value; + private Boolean expectedResult; + private String expectedError; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // subject under test + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + private Validator sut; + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // parameterized constructor + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + public ValidatorTest(String configcolumn, + String configValidation, + Object configValue, + String column, + String value, + Boolean expectedResult, + String expectedError) { + this.config = config(configcolumn, configValidation, configValue); + this.column = column; + this.value = value; + this.expectedResult = expectedResult; + this.expectedError = expectedError; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // init + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Before + public void initialize() { + sut = new Validator(config); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // tests + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Test + public void validation() { + // execution + ValidationState result = sut.isValid(column, value); + + // assertion + assertThat(result.isValid(), is(expectedResult)); + if (!expectedResult) { + assertThat(result.error(), is(expectedError)); + } + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // mocks + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + private Config config(String column, String validation, Object value) { + + Config columnConfig = mock(Config.class); + Config validatorConfig = mock(Config.class); + + when(columnConfig.hasPath(column)).thenReturn(true); + when(columnConfig.getConfig(column)).thenReturn(validatorConfig); + + when(validatorConfig.hasPath(validation)).thenReturn(true); + if (value instanceof Boolean) { + when(validatorConfig.getBoolean(validation)).thenReturn((Boolean) value); + } else if (value instanceof String) { + when(validatorConfig.getString(validation)).thenReturn((String) value); + } else if (value instanceof Integer) { + when(validatorConfig.getInt(validation)).thenReturn((Integer)value); + } + + return columnConfig; + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // parameters for tests + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + @Parameterized.Parameters + public static Collection validationConfigurations() { + return asList(new Object[][] { + { "column", "not empty", true, "column", "value", true, null }, + { "column", "not empty", true, "column", "", false, "should not be empty\n" }, + { "column", "not empty", true, "column", null, false, "should not be empty\n" }, + { "column", "integer", true, "column", "999", true, null }, + { "column", "integer", true, "column", "a", false, "should be an integer\n" }, + { "column", "minlength", 2, "column", "12", true, null }, + { "column", "minlength", 2, "column", "1", false, "has not min length of 2\n" }, + { "column", "maxlength", 2, "column", "12", true, null }, + { "column", "maxlength", 2, "column", "123", false, "has not max length of 2\n" }, + { "column", "date", "yyyyMMdd", "column", "20151127", true, null }, + { "column", "date", "yyyyMMdd", "column", "27.11.2015", false, "is not a date of format yyyyMMdd\n" }, + { "column", "alphanumeric", true, "column", "abcABC123", true, null }, + { "column", "alphanumeric", true, "column", "-abcABC123", false, "should not be alphanumeric\n" }, + { "column", "regexp", "[a-z]*", "column", "abc", true, null }, + { "column", "regexp", "[a-z]*", "column", "abcA", false, "does not match [a-z]*\n" }, + { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "abcdef", true, null }, + { "column", "groovy", "value.contains('a')? 'true' : 'no a inside'", "column", "bcdefg", false, "no a inside\n" }, + }); + } + + +} \ No newline at end of file