diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..ef226621 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,28 @@ +--- +name: Отчет об ошибке +about: Создайте отчет об ошибке, чтобы улучшить приложение +title: '' +labels: bug +assignees: '' + +--- + +**Опишите ошибку** +Ясное и краткое описание ошибки. + +**Последовательность действий для воспроизведения** +Шаги по воспроизведению ошибки. + +**Ожидаемый результат** +Ясное и краткое описание того, что вы ожидали. + +**Скриншоты** +Если возможно, добавьте скриншоты, чтобы объяснить проблему. + +**Окружение:** + - ОС: [например windows 10] + - Версия Java [например 19] + - Версия приложения[например 2020.1] + +**Дополнительный контекст** +Любой другой контекст проблемы. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..be84300d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Запрос новой функциональности +about: Предложите идею для этого проекта +title: '' +labels: feature +assignees: '' + +--- + +**Связан ли ваш запрос новой функциональности с проблемой? Пожалуйста, опишите.** +Четкое и краткое описание проблемы. Например, я всегда расстраиваюсь, когда [...] + +**Опишите желаемое решение** +Четкое и краткое описание того, что вы хотите сделать. + +**Опишите альтернативы, которые вы рассмотрели** +Четкое и краткое описание любых рассмотренных вами альтернативных решений или функций. + +**Дополнительный контекст** +Любой другой контекст. diff --git a/.github/workflows/unit-tests.yml b/.github/workflows/unit-tests.yml new file mode 100644 index 00000000..f02a97f6 --- /dev/null +++ b/.github/workflows/unit-tests.yml @@ -0,0 +1,45 @@ +name: Unit Tests + +on: + workflow_dispatch: + pull_request: + branches: + - 'master' + - 'develop' + push: + branches: + - 'master' + - 'develop' + +jobs: + tests: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Set up JDK + uses: actions/setup-java@v3 + with: + java-version: '18' + distribution: 'liberica' + cache: maven + + - name: Maven Tests + run: mvn --batch-mode clean test + + - name: Test Coverage + uses: codecov/codecov-action@v3 + + - name: SonarCloud Analyze + run: > + mvn --batch-mode sonar:sonar + -Dsonar.projectKey=spacious-team_table-wrapper-csv-impl + -Dsonar.organization=spacious-team + -Dsonar.host.url=https://sonarcloud.io + -Dsonar.login=$SONAR_TOKEN + -Dsonar.coverage.jacoco.xmlReportPaths=./target/site/jacoco/jacoco.xml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} diff --git a/.gitignore b/.gitignore index 70d1c01f..e0310597 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ target/ .mvn !.mvn/wrapper/maven-wrapper.properties -!.mvn/wrapper/MavenWrapperDownloader.java !**/src/main/** !**/src/test/** @@ -22,6 +21,7 @@ target/ !.idea/runConfigurations !.idea/codeStyles !.idea/copyright +!.idea/inspectionProfiles ### NetBeans ### /nbproject/private/ diff --git a/.idea/copyright/GNU_AGPLv3.xml b/.idea/copyright/GNU_AGPLv3.xml index 3ab412c0..3c847ce8 100644 --- a/.idea/copyright/GNU_AGPLv3.xml +++ b/.idea/copyright/GNU_AGPLv3.xml @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/.idea/inspectionProfiles/Inspections.xml b/.idea/inspectionProfiles/Inspections.xml new file mode 100644 index 00000000..48b605b3 --- /dev/null +++ b/.idea/inspectionProfiles/Inspections.xml @@ -0,0 +1,26 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 00000000..07fe6c0e --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index b901097f..00000000 --- a/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2007-present the original author or 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 - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if(mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if(mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if(!outputFile.getParentFile().exists()) { - if(!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a5..00000000 Binary files a/.mvn/wrapper/maven-wrapper.jar and /dev/null differ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index 642d572c..63f9ec6d 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1,2 +1,18 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar diff --git a/README.md b/README.md index f3e57797..24912fa9 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,19 @@ ![java-version](https://img.shields.io/badge/Java-11-brightgreen?style=flat-square) -![jitpack-last-release](https://jitpack.io/v/spacious-team/table-wrapper-csv-impl.svg?style=flat-square) +[![jitpack-last-release](https://jitpack.io/v/spacious-team/table-wrapper-csv-impl.svg?style=flat-square)]( +https://jitpack.io/#spacious-team/table-wrapper-csv-impl) +[![Unit tests](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fspacious-team%2Ftable-wrapper-csv-impl%2Fbadge%3Fref%3Ddevelop&style=flat-square&label=Test&logo=none)]( +https://github.com/spacious-team/table-wrapper-csv-impl/actions/workflows/unit-tests.yml) +[![Coverage](https://img.shields.io/codecov/c/github/spacious-team/table-wrapper-csv-impl/develop?label=Coverage&style=flat-square&token=abjh1TArzE)]( +https://codecov.io/gh/spacious-team/table-wrapper-csv-impl) ### Назначение Предоставляет реализацию `Table Wrapper API` для удобного доступа к табличным данным, сохраненным в файлах формата `csv`. + +Если не используется [Spring Boot Starter](https://github.com/spacious-team/table-wrapper-spring-boot-starter), +то сначала в реестр доступных фабрик нужно зарегистрировать фабрику csv таблиц: +```java +TableFactoryRegistry.add(new CsvTableFactory()); +``` Пример создания таблиц из файла `1.csv` ```java ReportPage reportPage = new CsvReportPage(Path.of("1.csv")); @@ -34,7 +45,7 @@ Table tableN = reportPage.create("Table N description", ...); ``` -и добавить зависимость +Далее следует добавить зависимость ```xml com.github.spacious-team @@ -45,4 +56,4 @@ Table tableN = reportPage.create("Table N description", ...); В качестве версии можно использовать: - версию [релиза](https://github.com/spacious-team/table-wrapper-csv-impl/releases) на github; - паттерн `-SNAPSHOT` для сборки зависимости с последнего коммита выбранной ветки; -- короткий 10-ти значный номер коммита для сборки зависимости с указанного коммита. +- короткий десяти значный номер коммита для сборки зависимости с указанного коммита. diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 00000000..fe09e4e0 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,8 @@ +# Workaround for any java version not supported by Jitpack.io yet +# https://github.com/jitpack/jitpack.io/issues/4260 +#before_install: +# - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh +# - source install-jdk.sh --feature 15 +# - jshell --version +jdk: + - openjdk11 \ No newline at end of file diff --git a/mvnw b/mvnw index 41c0f0c2..8d937f4c 100644 --- a/mvnw +++ b/mvnw @@ -19,7 +19,7 @@ # ---------------------------------------------------------------------------- # ---------------------------------------------------------------------------- -# Maven Start Up Batch script +# Apache Maven Wrapper startup batch script, version 3.2.0 # # Required ENV vars: # ------------------ @@ -27,7 +27,6 @@ # # Optional ENV vars # ----------------- -# M2_HOME - location of maven2's installed home dir # MAVEN_OPTS - parameters passed to the Java VM when running Maven # e.g. to debug Maven itself, use # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 @@ -36,6 +35,10 @@ if [ -z "$MAVEN_SKIP_RC" ] ; then + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + if [ -f /etc/mavenrc ] ; then . /etc/mavenrc fi @@ -50,7 +53,7 @@ fi cygwin=false; darwin=false; mingw=false -case "`uname`" in +case "$(uname)" in CYGWIN*) cygwin=true ;; MINGW*) mingw=true;; Darwin*) darwin=true @@ -58,9 +61,9 @@ case "`uname`" in # See https://developer.apple.com/library/mac/qa/qa1170/_index.html if [ -z "$JAVA_HOME" ]; then if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" + JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME else - export JAVA_HOME="/Library/Java/Home" + JAVA_HOME="/Library/Java/Home"; export JAVA_HOME fi fi ;; @@ -68,68 +71,38 @@ esac if [ -z "$JAVA_HOME" ] ; then if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` + JAVA_HOME=$(java-config --jre-home) fi fi -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - 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 - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - # For Cygwin, ensure paths are in UNIX format before anything is touched if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + JAVA_HOME=$(cygpath --unix "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` + CLASSPATH=$(cygpath --path --unix "$CLASSPATH") fi # For Mingw, ensure paths are in UNIX format before anything is touched if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && + JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" fi if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + javaExecutable="$(which javac)" + if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + readLink=$(which readlink) + if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + javaHome="$(dirname "\"$javaExecutable\"")" + javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" else - javaExecutable="`readlink -f \"$javaExecutable\"`" + javaExecutable="$(readlink -f "\"$javaExecutable\"")" fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` + javaHome="$(dirname "\"$javaExecutable\"")" + javaHome=$(expr "$javaHome" : '\(.*\)/bin') JAVA_HOME="$javaHome" export JAVA_HOME fi @@ -145,7 +118,7 @@ if [ -z "$JAVACMD" ] ; then JAVACMD="$JAVA_HOME/bin/java" fi else - JAVACMD="`which java`" + JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" fi fi @@ -159,12 +132,9 @@ if [ -z "$JAVA_HOME" ] ; then echo "Warning: JAVA_HOME environment variable is not set." fi -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - # traverses directory structure from process work directory to filesystem root # first directory with .mvn subdirectory is considered project base directory find_maven_basedir() { - if [ -z "$1" ] then echo "Path not specified to find_maven_basedir" @@ -180,96 +150,99 @@ find_maven_basedir() { fi # workaround for JBEAP-8937 (on Solaris 10/Sparc) if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` + wdir=$(cd "$wdir/.." || exit 1; pwd) fi # end of workaround done - echo "${basedir}" + printf '%s' "$(cd "$basedir" || exit 1; pwd)" } # concatenates all lines of a file concat_lines() { if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" + # Remove \r in case we run on Windows within Git Bash + # and check out the repository with auto CRLF management + # enabled. Otherwise, we may read lines that are delimited with + # \r\n and produce $'-Xarg\r' rather than -Xarg due to word + # splitting rules. + tr -s '\r\n' ' ' < "$1" + fi +} + +log() { + if [ "$MVNW_VERBOSE" = true ]; then + printf '%s\n' "$1" fi } -BASE_DIR=`find_maven_basedir "$(pwd)"` +BASE_DIR=$(find_maven_basedir "$(dirname "$0")") if [ -z "$BASE_DIR" ]; then exit 1; fi +MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR +log "$MAVEN_PROJECTBASEDIR" + ########################################################################################## # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central # This allows using the maven wrapper in projects that prohibit checking in binary data. ########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi +wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" +if [ -r "$wrapperJarPath" ]; then + log "Found $wrapperJarPath" else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi + log "Couldn't find $wrapperJarPath, downloading it ..." + if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + while IFS="=" read -r key value; do + # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) + safeValue=$(echo "$value" | tr -d '\r') + case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" + log "Downloading from: $wrapperUrl" + if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") fi if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi + log "Found wget ... using wget" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" + wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" + wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" fi elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi + log "Found curl ... using curl" + [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" fi - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + log "Falling back to using Java to download" + javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" + javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" # For Cygwin, switch paths to Windows format before running javac if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` + javaSource=$(cygpath --path --windows "$javaSource") + javaClass=$(cygpath --path --windows "$javaClass") fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") + if [ -e "$javaSource" ]; then + if [ ! -e "$javaClass" ]; then + log " - Compiling MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/javac" "$javaSource") fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + if [ -e "$javaClass" ]; then + log " - Running MavenWrapperDownloader.java ..." + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" fi fi fi @@ -278,33 +251,58 @@ fi # End of extension ########################################################################################## -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR +# If specified, validate the SHA-256 sum of the Maven wrapper jar file +wrapperSha256Sum="" +while IFS="=" read -r key value; do + case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; + esac +done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" +if [ -n "$wrapperSha256Sum" ]; then + wrapperSha256Result=false + if command -v sha256sum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + elif command -v shasum > /dev/null; then + if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then + wrapperSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." + echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." + exit 1 + fi + if [ $wrapperSha256Result = false ]; then + echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 + echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 + echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 + exit 1 + fi fi + MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" # For Cygwin, switch paths to Windows format before running java if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + CLASSPATH=$(cygpath --path --windows "$CLASSPATH") [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` + MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") fi # Provide a "standardized" way to retrieve the CLI args that will # work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" export MAVEN_CMD_LINE_ARGS WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain +# shellcheck disable=SC2086 # safe args exec "$JAVACMD" \ $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd index 86115719..c4586b56 100644 --- a/mvnw.cmd +++ b/mvnw.cmd @@ -18,13 +18,12 @@ @REM ---------------------------------------------------------------------------- @REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script +@REM Apache Maven Wrapper startup batch script, version 3.2.0 @REM @REM Required ENV vars: @REM JAVA_HOME - location of a JDK home dir @REM @REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven @@ -46,8 +45,8 @@ if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") @REM Execute a user defined script before this one if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre @REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* :skipRcPre @setlocal @@ -120,10 +119,10 @@ SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" +set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B ) @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central @@ -134,11 +133,11 @@ if exist %WRAPPER_JAR% ( ) ) else ( if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" + SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" ) if "%MVNW_VERBOSE%" == "true" ( echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% + echo Downloading from: %WRAPPER_URL% ) powershell -Command "&{"^ @@ -146,7 +145,7 @@ if exist %WRAPPER_JAR% ( "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ "}" if "%MVNW_VERBOSE%" == "true" ( echo Finished downloading %WRAPPER_JAR% @@ -154,11 +153,35 @@ if exist %WRAPPER_JAR% ( ) @REM End of extension +@REM If specified, validate the SHA-256 sum of the Maven wrapper jar file +SET WRAPPER_SHA_256_SUM="" +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B +) +IF NOT %WRAPPER_SHA_256_SUM%=="" ( + powershell -Command "&{"^ + "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ + "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ + " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ + " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ + " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ + " exit 1;"^ + "}"^ + "}" + if ERRORLEVEL 1 goto error +) + @REM Provide a "standardized" way to retrieve the CLI args that will @REM work with both Windows and non-Windows executions. set MAVEN_CMD_LINE_ARGS=%* -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* if ERRORLEVEL 1 goto error goto end @@ -168,15 +191,15 @@ set ERROR_CODE=1 :end @endlocal & set ERROR_CODE=%ERROR_CODE% -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost @REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" :skipRcPost @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause +if "%MAVEN_BATCH_PAUSE%"=="on" pause -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% -exit /B %ERROR_CODE% +cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml index 0e20bab4..71db352e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,7 +1,7 @@ + + + org.junit.jupiter + junit-jupiter + 5.9.2 + test + + + org.mockito + mockito-junit-jupiter + 5.2.0 + test + + + nl.jqno.equalsverifier + equalsverifier + 3.14.1 + test + + + org.slf4j + slf4j-api + 2.0.7 + test + + + + + maven-surefire-plugin + 2.22.2 + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.10.1 + + true + true + true + + + org.projectlombok + lombok + ${lombok.version} + + + org.checkerframework + checker + ${checkerframework.version} + + + + + lombok.launch.AnnotationProcessorHider$AnnotationProcessor + + + org.checkerframework.checker.nullness.NullnessChecker + + + + + -AskipDefs=.*Test + -J--add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED + -J--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED + -J--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED + + + + + org.jacoco + jacoco-maven-plugin + 0.8.8 + + + prepare-agent + + prepare-agent + + + + report + test + + report + + + + org.apache.maven.plugins maven-source-plugin @@ -104,4 +206,5 @@ + \ No newline at end of file diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvCellDataAccessObject.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvCellDataAccessObject.java index 554c5c43..68623e56 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvCellDataAccessObject.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvCellDataAccessObject.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,46 +18,42 @@ package org.spacious_team.table_wrapper.csv; -import lombok.Getter; -import lombok.Setter; +import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.CellDataAccessObject; -import org.spacious_team.table_wrapper.csv.CsvTableCell.RowAndIndex; +import org.spacious_team.table_wrapper.api.InstantParser; import java.time.Instant; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.ZoneOffset; -import java.time.format.DateTimeFormatter; +import java.time.LocalTime; -public class CsvCellDataAccessObject implements CellDataAccessObject { - public static final CsvCellDataAccessObject INSTANCE = new CsvCellDataAccessObject(); - @Setter - @Getter - public static DateTimeFormatter dateTimeFormatter = null; +import static java.util.Objects.requireNonNull; + +@ToString +@EqualsAndHashCode +@RequiredArgsConstructor(staticName = "of") +public class CsvCellDataAccessObject implements CellDataAccessObject { + public static final CsvCellDataAccessObject INSTANCE = CsvCellDataAccessObject.of( + InstantParser.builder().defaultTime(LocalTime.NOON).build()); + private final InstantParser instantParser; @Override - public RowAndIndex getCell(CsvTableRow row, Integer cellIndex) { - return row.getCell(cellIndex).getRowAndIndex(); + public @Nullable String getCell(CsvTableRow row, Integer cellIndex) { + //noinspection ConstantConditions + return (cellIndex == null) ? null : row.getCellValue(cellIndex); } @Override - public String getValue(RowAndIndex cell) { - int columnIndex = cell.getColumnIndex(); - String[] row = cell.getRow(); - return (columnIndex < row.length) ? row[columnIndex] : null; + public @Nullable String getValue(@Nullable String cell) { + return cell; } @Override - public Instant getInstantValue(RowAndIndex cell) { - String value = getValue(cell); - DateTimeFormatter formatter = (dateTimeFormatter != null) ? - dateTimeFormatter : - DateTimeFormatParser.getFor(value); - LocalDateTime dateTime = (value.length() == 10) ? - LocalDate.parse(value, formatter).atTime(12, 0) : - LocalDateTime.parse(value, formatter); - return dateTime - .atZone(ZoneOffset.systemDefault()) - .toInstant(); + public Instant getInstantValue(@Nullable String cell) { + @Nullable String value = getValue(cell); + @SuppressWarnings("nullness") + String nonNullValue = requireNonNull(value, "Not an instant"); + return instantParser.parseInstant(nonNullValue); } } diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvReportPage.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvReportPage.java index 3ab8a1c3..bf06c9f1 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvReportPage.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvReportPage.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,10 +18,9 @@ package org.spacious_team.table_wrapper.csv; -import com.univocity.parsers.common.record.Record; import com.univocity.parsers.csv.CsvParser; import com.univocity.parsers.csv.CsvParserSettings; -import lombok.RequiredArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.AbstractReportPage; import org.spacious_team.table_wrapper.api.TableCellAddress; @@ -33,7 +32,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; -import java.util.function.BiPredicate; import java.util.function.Predicate; import static java.nio.charset.StandardCharsets.UTF_8; @@ -43,10 +41,17 @@ public class CsvReportPage extends AbstractReportPage { private final String[][] rows; /** - * Field and line delimiter detected automatically. UTF-8 encoding file expected. + * Field and line delimiter detected automatically. UTF-8 encoded file expected. */ public CsvReportPage(Path path) throws IOException { - this(Files.newInputStream(path, StandardOpenOption.READ), UTF_8, getDefaultCsvParserSettings()); + this(Files.newInputStream(path, StandardOpenOption.READ)); + } + + /** + * Closes inputStream if success. UTF-8 encoded stream data expected. + */ + public CsvReportPage(InputStream inputStream) throws IOException { + this(inputStream, UTF_8, getDefaultCsvParserSettings()); } /** @@ -75,13 +80,14 @@ public TableCellAddress find(Object value, int startRow, int endRow, int startCo } @Override - public TableCellAddress find(int startRow, int endRow, int startColumn, int endColumn, Predicate cellValuePredicate) { + public TableCellAddress find(int startRow, int endRow, int startColumn, int endColumn, + Predicate<@Nullable Object> cellValuePredicate) { return CsvTableHelper.find(rows, startRow, endRow, startColumn, endColumn, cellValuePredicate::test); } @Override - public CsvTableRow getRow(int i) { - return (i >= rows.length) ? null : new CsvTableRow(rows[i], i); + public @Nullable CsvTableRow getRow(int i) { + return (i < 0 || i >= rows.length) ? null : CsvTableRow.of(rows[i], i); } @Override diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTable.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTable.java index bdcf2d96..e70264a1 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTable.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTable.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,31 +18,35 @@ package org.spacious_team.table_wrapper.csv; -import lombok.AccessLevel; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.Setter; import lombok.ToString; import org.spacious_team.table_wrapper.api.AbstractReportPage; import org.spacious_team.table_wrapper.api.AbstractTable; import org.spacious_team.table_wrapper.api.CellDataAccessObject; import org.spacious_team.table_wrapper.api.Table; import org.spacious_team.table_wrapper.api.TableCellRange; -import org.spacious_team.table_wrapper.api.TableColumnDescription; +import org.spacious_team.table_wrapper.api.TableHeaderColumn; @ToString(callSuper = true) -public class CsvTable extends AbstractTable { +@EqualsAndHashCode(callSuper = true) +public class CsvTable extends AbstractTable { - @Getter(AccessLevel.PROTECTED) - private final CellDataAccessObject cellDataAccessObject = CsvCellDataAccessObject.INSTANCE; + @Setter + @Getter + private CellDataAccessObject cellDataAccessObject = CsvCellDataAccessObject.INSTANCE; - protected CsvTable(AbstractReportPage reportPage, - String tableName, - TableCellRange tableRange, - Class headerDescription, - int headersRowCount) { + protected & TableHeaderColumn> + CsvTable(AbstractReportPage reportPage, + String tableName, + TableCellRange tableRange, + Class headerDescription, + int headersRowCount) { super(reportPage, tableName, tableRange, headerDescription, headersRowCount); } - public CsvTable(AbstractTable table, int appendDataRowsToTop, int appendDataRowsToBottom) { + protected CsvTable(AbstractTable table, int appendDataRowsToTop, int appendDataRowsToBottom) { super(table, appendDataRowsToTop, appendDataRowsToBottom); } diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableCell.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableCell.java index 91fe976c..3b85ec7b 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableCell.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableCell.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,33 +18,47 @@ package org.spacious_team.table_wrapper.csv; +import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.RequiredArgsConstructor; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.AbstractTableCell; +import org.spacious_team.table_wrapper.api.EmptyTableCell; +import org.spacious_team.table_wrapper.api.TableCell; -public class CsvTableCell extends AbstractTableCell { +@ToString +@EqualsAndHashCode(callSuper = true) +public class CsvTableCell extends AbstractTableCell { @Getter - private final RowAndIndex rowAndIndex; + private final int columnIndex; + private final String value; - public static CsvTableCell of(String[] row, int columnIndex) { - return new CsvTableCell(new RowAndIndex(row, columnIndex)); + public static TableCell of(String[] row, int columnIndex) { + return of(row, columnIndex, CsvCellDataAccessObject.INSTANCE); } - public CsvTableCell(RowAndIndex rowAndIndex) { - super(rowAndIndex, CsvCellDataAccessObject.INSTANCE); - this.rowAndIndex = rowAndIndex; + public static TableCell of(String[] row, int columnIndex, CsvCellDataAccessObject dao) { + @Nullable String cellValue = getCellValue(row, columnIndex); + return cellValue == null ? + EmptyTableCell.of(columnIndex) : + new CsvTableCell(cellValue, columnIndex, dao); } - @Override - public int getColumnIndex() { - return rowAndIndex.getColumnIndex(); + private static @Nullable String getCellValue(String[] row, int columnIndex) { + return (columnIndex >= 0) && (columnIndex < row.length) ? + row[columnIndex] : + null; } - @Getter - @RequiredArgsConstructor - static class RowAndIndex { - final String[] row; - final int columnIndex; + private CsvTableCell(String cellValue, int columnIndex, CsvCellDataAccessObject dao) { + super(cellValue, dao); + this.value = cellValue; + this.columnIndex = columnIndex; + } + + @Override + protected CsvTableCell createWithCellDataAccessObject(CsvCellDataAccessObject dao) { + return new CsvTableCell(value, columnIndex, dao); } } diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableFactory.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableFactory.java index 54d6b01e..ac74759b 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableFactory.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableFactory.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2022 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,12 +18,15 @@ package org.spacious_team.table_wrapper.csv; +import lombok.ToString; import org.spacious_team.table_wrapper.api.AbstractTableFactory; import org.spacious_team.table_wrapper.api.ReportPage; import org.spacious_team.table_wrapper.api.Table; import org.spacious_team.table_wrapper.api.TableCellRange; -import org.spacious_team.table_wrapper.api.TableColumnDescription; +import org.spacious_team.table_wrapper.api.TableHeaderColumn; +@SuppressWarnings("unused") +@ToString(callSuper = true) public class CsvTableFactory extends AbstractTableFactory { public CsvTableFactory() { @@ -31,11 +34,12 @@ public CsvTableFactory() { } @Override - public Table create(ReportPage reportPage, - String tableName, - TableCellRange tableRange, - Class headerDescription, - int headersRowCount) { + public & TableHeaderColumn> + Table create(ReportPage reportPage, + String tableName, + TableCellRange tableRange, + Class headerDescription, + int headersRowCount) { return new CsvTable( cast(reportPage), tableName, diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableHelper.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableHelper.java index 03732db4..c7317e57 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableHelper.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableHelper.java @@ -1,6 +1,6 @@ /* - * Table Wrapper Xml SpreadsheetML Impl - * Copyright (C) 2020 Vitalii Ananev + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as @@ -18,24 +18,27 @@ package org.spacious_team.table_wrapper.csv; +import lombok.NoArgsConstructor; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.TableCellAddress; import java.util.Objects; -import java.util.function.BiPredicate; import java.util.function.Predicate; +import static lombok.AccessLevel.PRIVATE; import static org.spacious_team.table_wrapper.api.TableCellAddress.NOT_FOUND; -class CsvTableHelper { +@NoArgsConstructor(access = PRIVATE) +final class CsvTableHelper { - static TableCellAddress find(String[][] table, Object expected, + static TableCellAddress find(String[][] table, @Nullable Object expected, int startRow, int endRow, int startColumn, int endColumn) { return find(table, startRow, endRow, startColumn, endColumn, equalsPredicate(expected)); } static TableCellAddress find(String[][] table, int startRow, int endRow, int startColumn, int endColumn, - Predicate predicate) { + Predicate<@Nullable String> predicate) { startRow = Math.max(0, startRow); endRow = Math.min(endRow, table.length); for (int rowNum = startRow; rowNum < endRow; rowNum++) { @@ -48,19 +51,20 @@ static TableCellAddress find(String[][] table, int startRow, int endRow, int sta return NOT_FOUND; } - static TableCellAddress find(String[] row, int rowNum, int startColumn, int endColumn, Predicate predicate) { + static TableCellAddress find(String[] row, int rowNum, int startColumn, int endColumn, + Predicate<@Nullable String> predicate) { startColumn = Math.max(0, startColumn); endColumn = Math.min(endColumn, row.length); for (int i = startColumn; i < endColumn; i++) { String cell = row[i]; if (predicate.test(cell)) { - return new TableCellAddress(rowNum, i); + return TableCellAddress.of(rowNum, i); } } return NOT_FOUND; } - static Predicate equalsPredicate(Object expected) { + static Predicate<@Nullable String> equalsPredicate(@Nullable Object expected) { if (expected == null) { return Objects::isNull; } diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableRow.java b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableRow.java index fa68094a..288e6d1a 100644 --- a/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableRow.java +++ b/src/main/java/org/spacious_team/table_wrapper/csv/CsvTableRow.java @@ -1,35 +1,61 @@ +/* + * Table Wrapper CSV Impl + * Copyright (C) 2022 Spacious Team + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + package org.spacious_team.table_wrapper.csv; -import com.univocity.parsers.common.record.Record; +import lombok.EqualsAndHashCode; import lombok.Getter; +import lombok.ToString; +import org.checkerframework.checker.nullness.qual.Nullable; import org.spacious_team.table_wrapper.api.AbstractReportPageRow; import org.spacious_team.table_wrapper.api.TableCell; -import org.spacious_team.table_wrapper.api.TableCellAddress; import java.util.Iterator; +import java.util.NoSuchElementException; import static org.spacious_team.table_wrapper.api.TableCellAddress.NOT_FOUND; import static org.spacious_team.table_wrapper.csv.CsvTableHelper.equalsPredicate; +@ToString(of = "rowNum") +@EqualsAndHashCode(of = {"rowNum", "row"}, callSuper = false) public class CsvTableRow extends AbstractReportPageRow { private final String[] row; @Getter private final int rowNum; - private final CsvTableCell[] cellsCache; + private final TableCell[] cellsCache; + + public static CsvTableRow of(String[] row, int rowNum) { + return new CsvTableRow(row, rowNum); + } - public CsvTableRow(String[] row, int rowNum) { + private CsvTableRow(String[] row, int rowNum) { this.row = row; this.rowNum = rowNum; - this.cellsCache = new CsvTableCell[row.length]; + this.cellsCache = new TableCell[row.length]; } @Override - public CsvTableCell getCell(int i) { - if (i >= row.length) { + public @Nullable TableCell getCell(int i) { + if (i < 0 || i >= row.length) { return null; } - CsvTableCell cell = cellsCache[i]; + TableCell cell = cellsCache[i]; if (cell == null) { cell = CsvTableCell.of(row, i); cellsCache[i] = cell; @@ -37,6 +63,10 @@ public CsvTableCell getCell(int i) { return cell; } + @Nullable String getCellValue(int i) { + return (i < 0 || i >= row.length) ? null : row[i]; + } + @Override public int getFirstCellNum() { return (row.length > 0) ? 0 : -1; @@ -48,13 +78,13 @@ public int getLastCellNum() { } @Override - public boolean rowContains(Object value) { + public boolean rowContains(@Nullable Object value) { return CsvTableHelper.find(row, rowNum, 0, row.length, equalsPredicate(value)) != NOT_FOUND; } @Override - public Iterator iterator() { - return new Iterator<>() { + public Iterator<@Nullable TableCell> iterator() { + return new Iterator<@Nullable TableCell>() { private int cellIndex = 0; @Override @@ -63,8 +93,11 @@ public boolean hasNext() { } @Override - public TableCell next() { - return getCell(cellIndex++); + public @Nullable TableCell next() { + if (hasNext()) { + return getCell(cellIndex++); + } + throw new NoSuchElementException(); } }; } diff --git a/src/main/java/org/spacious_team/table_wrapper/csv/DateTimeFormatParser.java b/src/main/java/org/spacious_team/table_wrapper/csv/DateTimeFormatParser.java deleted file mode 100644 index 01ea1973..00000000 --- a/src/main/java/org/spacious_team/table_wrapper/csv/DateTimeFormatParser.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Коммерческая тайна общества с ограниченной ответственностью «МФИ Софт» (ООО «МФИ Софт»), - * место нахождения - Российская Федерация, 603126, г. Нижний Новгород, ул. Родионова, дом 192, корпус 1; - * (с) ООО «МФИ Софт», 2021. - * ПРИМЕЧАНИЕ: Информация, интеллектуальные и технические концепции, содержащиеся здесь, принадлежат ООО «МФИ Софт», - * защищены действующим законодательством РФ. - * Их распространение без получения предварительного письменного согласия со стороны ООО «МФИ Софт» запрещено. - */ - -package org.spacious_team.table_wrapper.csv; - -import java.time.format.DateTimeFormatter; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -class DateTimeFormatParser { - - // internal calculated unique key -> date-time format - private static final Map dateTimeFormatters = new ConcurrentHashMap<>(); - - static DateTimeFormatter getFor(String dateTime) { - return (dateTime.length() == 10) ? - getForDate(dateTime) : - getForDateTime(dateTime); - } - - static DateTimeFormatter getForDate(String date) { - boolean isYearAtFirst; - char dateSplitter; - char ch = date.charAt(date.length() - 5); - if (!Character.isDigit(ch)) { - // date format is DD MM YYYY - isYearAtFirst = false; - dateSplitter = ch; - } else { - // date format is YYYY MM DD - isYearAtFirst = true; - dateSplitter = date.charAt(date.length() - 3); - } - return getDateFormatter(isYearAtFirst, dateSplitter); - } - - static DateTimeFormatter getForDateTime(String dateTime) { - boolean isDateAtFirst, isYearAtFirst; - char dateSplitter; - if (dateTime.charAt(2) == ':') { - // format is