From b3636d9df3c828d345cab3393de81eded5b8f0e3 Mon Sep 17 00:00:00 2001 From: Alexis Date: Mon, 22 Apr 2019 16:19:30 +0200 Subject: [PATCH] Released 1.0.0 --- .travis.yml | 15 + CHANGELOG.md | 3 + LICENSE => LICENSE.txt | 2 +- README.md | 167 ++-- logo.png | Bin 0 -> 13392 bytes pom.xml | 196 +++- .../alexisjehan/mender/api/MendCandidate.java | 46 + .../alexisjehan/mender/api/MendException.java | 50 + .../alexisjehan/mender/api/MendResult.java | 56 ++ .../github/alexisjehan/mender/api/Mender.java | 54 + .../api/evaluators/ConstraintEvaluator.java | 74 ++ .../api/evaluators/EstimationEvaluator.java | 85 ++ .../mender/api/evaluators/Evaluator.java | 41 + .../mender/api/evaluators/package-info.java | 28 + .../alexisjehan/mender/api/package-info.java | 28 + .../mender/dsv/DsvMendCandidate.java | 107 ++ .../alexisjehan/mender/dsv/DsvMendResult.java | 128 +++ .../alexisjehan/mender/dsv/DsvMender.java | 626 ++++++++++++ .../alexisjehan/mender/dsv/package-info.java | 29 + src/main/java/examples/BombFixExample.java | 57 -- src/main/java/examples/ConcreteExample.java | 82 ++ src/main/java/examples/DetailsExample.java | 60 -- .../java/examples/MissingCellsExample.java | 62 -- src/main/java/examples/NotQuotedExample.java | 64 -- src/main/java/examples/OptimizeExample.java | 67 ++ src/main/java/examples/SimpleExample.java | 86 ++ src/main/java/org/mender/Evaluator.java | 60 -- src/main/java/org/mender/Mender.java | 49 - src/main/java/org/mender/MenderException.java | 46 - .../java/org/mender/criteria/Constraint.java | 79 -- .../java/org/mender/criteria/Criterion.java | 42 - .../java/org/mender/criteria/Estimation.java | 107 -- .../org/mender/criteria/package-info.java | 29 - src/main/java/org/mender/dsv/DsvBuilder.java | 602 ----------- .../java/org/mender/dsv/DsvEvaluator.java | 148 --- src/main/java/org/mender/dsv/DsvMender.java | 544 ---------- src/main/java/org/mender/dsv/DsvReader.java | 163 --- src/main/java/org/mender/dsv/DsvUtils.java | 106 -- .../java/org/mender/dsv/package-info.java | 38 - src/main/java/org/mender/package-info.java | 30 - src/main/resources/missing_cells.tsv | 5 - src/main/resources/not_quoted.csv | 6 - .../mender/api/MendExceptionTest.java | 48 + .../evaluators/ConstraintEvaluatorTest.java | 54 + .../evaluators/EstimationEvaluatorTest.java | 68 ++ .../mender/dsv/DsvMendCandidateTest.java | 93 ++ .../mender/dsv/DsvMendResultTest.java | 106 ++ .../mender/dsv/DsvMenderBuilderTest.java | 216 ++++ .../alexisjehan/mender/dsv/DsvMenderTest.java | 153 +++ .../org/mender/criteria/ConstraintTest.java | 59 -- .../org/mender/criteria/EstimationTest.java | 87 -- .../java/org/mender/dsv/DsvBuilderTest.java | 938 ------------------ .../java/org/mender/dsv/DsvEvaluatorTest.java | 122 --- .../java/org/mender/dsv/DsvMenderTest.java | 169 ---- .../java/org/mender/dsv/DsvReaderTest.java | 77 -- .../java/org/mender/dsv/DsvUtilsTest.java | 81 -- 56 files changed, 2619 insertions(+), 3919 deletions(-) create mode 100644 .travis.yml create mode 100644 CHANGELOG.md rename LICENSE => LICENSE.txt (96%) create mode 100644 logo.png create mode 100644 src/main/java/com/github/alexisjehan/mender/api/MendCandidate.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/MendException.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/MendResult.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/Mender.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluator.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluator.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/evaluators/Evaluator.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/evaluators/package-info.java create mode 100644 src/main/java/com/github/alexisjehan/mender/api/package-info.java create mode 100644 src/main/java/com/github/alexisjehan/mender/dsv/DsvMendCandidate.java create mode 100644 src/main/java/com/github/alexisjehan/mender/dsv/DsvMendResult.java create mode 100644 src/main/java/com/github/alexisjehan/mender/dsv/DsvMender.java create mode 100644 src/main/java/com/github/alexisjehan/mender/dsv/package-info.java delete mode 100644 src/main/java/examples/BombFixExample.java create mode 100644 src/main/java/examples/ConcreteExample.java delete mode 100644 src/main/java/examples/DetailsExample.java delete mode 100644 src/main/java/examples/MissingCellsExample.java delete mode 100644 src/main/java/examples/NotQuotedExample.java create mode 100644 src/main/java/examples/OptimizeExample.java create mode 100644 src/main/java/examples/SimpleExample.java delete mode 100644 src/main/java/org/mender/Evaluator.java delete mode 100644 src/main/java/org/mender/Mender.java delete mode 100644 src/main/java/org/mender/MenderException.java delete mode 100644 src/main/java/org/mender/criteria/Constraint.java delete mode 100644 src/main/java/org/mender/criteria/Criterion.java delete mode 100644 src/main/java/org/mender/criteria/Estimation.java delete mode 100644 src/main/java/org/mender/criteria/package-info.java delete mode 100644 src/main/java/org/mender/dsv/DsvBuilder.java delete mode 100644 src/main/java/org/mender/dsv/DsvEvaluator.java delete mode 100644 src/main/java/org/mender/dsv/DsvMender.java delete mode 100644 src/main/java/org/mender/dsv/DsvReader.java delete mode 100644 src/main/java/org/mender/dsv/DsvUtils.java delete mode 100644 src/main/java/org/mender/dsv/package-info.java delete mode 100644 src/main/java/org/mender/package-info.java delete mode 100644 src/main/resources/missing_cells.tsv delete mode 100644 src/main/resources/not_quoted.csv create mode 100644 src/test/java/com/github/alexisjehan/mender/api/MendExceptionTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluatorTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluatorTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/dsv/DsvMendCandidateTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/dsv/DsvMendResultTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderBuilderTest.java create mode 100644 src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderTest.java delete mode 100644 src/test/java/org/mender/criteria/ConstraintTest.java delete mode 100644 src/test/java/org/mender/criteria/EstimationTest.java delete mode 100644 src/test/java/org/mender/dsv/DsvBuilderTest.java delete mode 100644 src/test/java/org/mender/dsv/DsvEvaluatorTest.java delete mode 100644 src/test/java/org/mender/dsv/DsvMenderTest.java delete mode 100644 src/test/java/org/mender/dsv/DsvReaderTest.java delete mode 100644 src/test/java/org/mender/dsv/DsvUtilsTest.java diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..9f2644c --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: java + +cache: + directories: + - $HOME/.m2 + +install: mvn install -DskipTests -Dmaven.javadoc.skip=true -Dgpg.skip + +jdk: + - oraclejdk11 + - openjdk11 + - openjdk12 + +after_success: + - mvn jacoco:report && bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..2e9ba7a --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 _(xxx)_ + +First release \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 96% rename from LICENSE rename to LICENSE.txt index 331c482..453f7c9 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2017 Alexis Jehan +Copyright (c) 2017-2019 Alexis Jehan Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ba6b9c0..249fe44 100644 --- a/README.md +++ b/README.md @@ -1,123 +1,150 @@ +![DSV Mender](logo.png) # DSV Mender +[![Maven Central](https://img.shields.io/maven-central/v/com.github.alexisjehan/dsv-mender.svg)](https://mvnrepository.com/artifact/com.github.alexisjehan/dsv-mender/latest) +[![Javadoc](http://www.javadoc.io/badge/com.github.alexisjehan/dsv-mender.svg)](http://www.javadoc.io/doc/com.github.alexisjehan/dsv-mender) +[![Travis](https://img.shields.io/travis/alexisjehan/dsv-mender.svg)](https://travis-ci.org/alexisjehan/dsv-mender) +[![Codecov](https://img.shields.io/codecov/c/github/alexisjehan/dsv-mender.svg)](https://codecov.io/gh/alexisjehan/dsv-mender) +[![License](https://img.shields.io/github/license/alexisjehan/dsv-mender.svg)](https://github.com/alexisjehan/dsv-mender/blob/master/LICENSE.txt) -A Java 8 library to fix malformed Delimiter-separated values (DSV) data automatically. +A Java 11+ library to fix malformed DSV (Delimiter-Separated Values) data automatically. ## Introduction - As many developers you may already had to treat some input data with formats such as _CSV_ or _JSON_. Sometimes that -task could become tricky to achieve because some values are not formatted how they are supposed to be. DSV Mender is a -library that aims to help you in such cases efficiently. Basically it collects some features from each valid column of -the data independently to find the best solution while handling invalid or missing values. +task could become tricky to achieve because some values are not always formatted how they are supposed to be. +**DSV Mender** is a library that aims to help you in such cases efficiently. Basically it collects some features from +each valid column of the data independently to find the best solution while handling invalid or missing values. -### Estimations and Constraints +### Constraints and estimations +DSV Mender is working with a concept of constraints and estimations that are associated to specific columns of the data: -DSV Mender is working with concepts of estimations and constraints that are assigned to desired columns: +* **Constraints** eliminate some candidate possibilities of a malformed row if they do not respect a rule, without +taking into account previous valid values at all. +For example if the third column has to be exactly 5 characters long, then all candidates with a value that does not +will be discarded. * **Estimations** could be used to collect some features from valid values. When an invalid value need to be fixed then the closest generated possibility is chosen. For example if you collect the length of valid values and get 5 characters 95% of the time then a possible fixed-value -that got a length of 5 got more chances to be selected than a possibility of 3 characters. +that got a length of 5 got more chances to be selected than a candidate of 3 characters. + +## Getting started +To include and use DSV Mender, you need to add the following dependency into your _Maven_ _pom.xml_ file: +```xml + + com.github.alexisjehan + dsv-mender + 1.0.0 + +``` -* **Constraints** unlike estimations, eliminate some possibilities if they do not respect a precise rule, without taking -into account valid values at all. -For example if the third column has to be exactly 5 characters long, then all possibilities with a value that does not -will be discarded. +Or if you are using _Gradle_: +```xml +dependencies { + compile "com.github.alexisjehan:dsv-mender:1.0.0" +} +``` -## Example +Also the Javadoc can be accessed [here](http://www.javadoc.io/doc/com.github.alexisjehan/dsv-mender). +## Examples Let's illustrate how it works step-by-step, consider the following CSV data: ```csv -ID,NAME,DESCRIPTION,BIRTHDAY,COUNTRY -1,John,Hey everyone I'm the first user,1984-05-16,United Kingdom -2,Pierre,Bonjour à tous vous allez bien ?,1992-11-26,France -3,Pedro,Holà qué tal ?,1962-01-05,Spain -4,Arnold,My country name contains a , in it,1974-05-30,Macedonia, Rep. of -5,Peter,I, like, to, use, commas, between, words,1994-12-04,United States +Release,Release date,Highlights +Java SE 9,2017-09-21,Initial release +Java SE 9.0.1,2017-10-17,October 2017 security fixes and critical bug fixes +Java SE 9.0.4,2018-01-16,Final release for JDK 9; January 2018 security fixes and critical bug fixes +Java SE 10,2018-03-20,Initial release +Java SE 10.0.1,2018-04-17,Security fixes, 5 bug fixes +Java SE 11,2018-09-25,Initial release +Java SE 11.0.1,2018-10-16,Security & bug fixes +Java SE 11.0.2,2019-01-15,Security & bug fixes +Java SE 12,Initial release ``` -As you can see, it looks like CSV data but values are not quoted. Have a look especially to the two last lines, yeah... -some values appear to contain some comma characters, which is used also as the delimiter. How to fix it ? Let's see how -DSV Mender works... +As you may see, some lines are not well-formatted. The "Java SE 10.0.1" "Highlights" column contains the delimiter +character, and the "Java SE 12" "Release date" column is missing. Let's see how to use DSV Mender to fix it. ### Building the mender +First you need to create a _Mender_ object based on the input data. That requires to specify the delimiter string as +well as the expected number of columns. -The first thing is to configure a _Mender_ object based on the input data. You need to specify the delimiter string as -well as the valid number of columns. - -#### Automatic configuration - -If you don't know so much about the data or you want to see how the Mender acts automatically you can use that: +#### Basic configuration +The lazy way, for a first attempt is to build a basic _Mender_, that can be able to mend most of input data: ```java -final DsvMender mender = DsvMender.auto(",", 5); // delimiter and number of columns +final var delimiter = ','; +final var length = 3; +final var mender = DsvMender.basic(delimiter, length); ``` #### Advanced configuration - -If you know approximately how some columns need to be formatted and to get more accurate results, you would better use a -more advanced configuration. Concerning our example the Mender could be created like this: +For more accurate results, you can also build a _Mender_ with custom _Constraints_ and _Estimations_. For our example above +we will use the following ones: ```java -final DsvMender mender = DsvMender.builder(",", 5) - .withLengthEstimations() // Estimating the length of the value for every columns - .withContainsEstimations(" ") // Estimating if the value contains a space character for every columns - .withPatternConstraint(0, Pattern.compile("[0-9]+")) // The ID column is always numerical, not empty - .withLengthConstraint(3, 10) // The birthday column always contains 10 characters +final var mender = DsvMender.builder() + .withDelimiter(',') + .withLength(3) + .withConstraint(value -> value.startsWith("Java SE"), 0) // values[0] must start with "Java SE" + .withConstraint(value -> value.isEmpty() || 10 == value.length(), 1)// values[1] must be empty or have a length of 10 .build(); ``` ### Processing the data - -Before to fix, and because we configured estimations, then we first need to fit the DSV Mender with valid rows. - -```java -mender.fit("1,John,Hey everyone I'm the first user,1984-05-16,United Kingdom"); -mender.fit("2,Pierre,Bonjour à tous vous allez bien ?,1992-11-26,France"); -mender.fit("3,Pedro,Holà, qué tal ?,1962-01-05,Spain"); -``` - -Finally we can now fix invalid rows and display the result: +Once you got your _Mender_ component built, you are able to process your data line by line. Note that you do not have to +worry of the passed line being valid or not, if it is then the _Mender_ will still fit its _Estimations_ before to +return it. ```java -try { - Arrays.asList(mender.fix("4,Arnold,My country name contains a , in it,1974-05-30,Macedonia, Rep. of")).forEach(System.out::println); - System.out.println(); - Arrays.asList(mender.fix("5,Peter,I, like, to, use, commas, between, words,1994-12-04,United States")).forEach(System.out::println); -} catch (final MenderException e) { - System.err.println("ERROR: No solution has been found, try others estimations and constraints"); +String row; +while (null != (row = reader.readLine())) { + printValues(mender.mend(row)); } ``` -If you had properly configured the DSV Mender as described earlier, then the data should be fixed. +Finally here is the result we got for our example: -#### Notes: -* If you don't know which row is valid or not, you should use _fitIfValid_ and _fixIfNotValid_ instead of _fit_ and -_fix_. -* Even better, you can use the _DSVReader_ wrapper class that automatically fit and fix while reading from a source. - -More examples can be found in the _examples_ package. - -## Maven commands +``` +"Release", "Release date", "Highlights" +"Java SE 9", "2017-09-21", "Initial release" +"Java SE 9.0.1", "2017-10-17", "October 2017 security fixes and critical bug fixes" +"Java SE 9.0.4", "2018-01-16", "Final release for JDK 9; January 2018 security fixes and critical bug fixes" +"Java SE 10", "2018-03-20", "Initial release" +"Java SE 10.0.1", "2018-04-17", "Security fixes, 5 bug fixes" +"Java SE 11", "2018-09-25", "Initial release" +"Java SE 11.0.1", "2018-10-16", "Security & bug fixes" +"Java SE 11.0.2", "2019-01-15", "Security & bug fixes" +"Java SE 12", "", "Initial release" +``` -### Compiling +(You can find the code of that example among others in the "examples" package) +## Maven phases and goals +Compile, test and install the JAR in the local Maven repository: ``` -mvn compile +mvn install ``` -### Running unit tests - +Run JUnit 5 tests: ``` mvn test ``` -### Generating the Javadoc - +Generate the Javadoc API documentation: ``` mvn javadoc:javadoc ``` -## License +Update sources license: +``` +mvn license:format +``` + +Generate the Jacoco test coverage report: +``` +mvn jacoco:report +``` -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details \ No newline at end of file +## License +This project is licensed under the MIT License - see the [LICENSE](LICENSE.txt) file for details \ No newline at end of file diff --git a/logo.png b/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5ec2cc0c1261155938d66518d701a4e6d41d7901 GIT binary patch literal 13392 zcmZvD2UHVX*LCO!0tzb9K|rLd^xj08bfkkKy+i0y1rY&(7@A0xB1nnS2^|#)Eupv2 zlt5^qcLe?k&-10D(YM8tRV>Kp+As{69G< z@Xgw9lWpKY`a(UB%*Y-_Td>5I|0nTK-rF~!aLCJdATmHUx z?2+wp2zz>BC3RCs@6gEFPGnmKxn0d1l-e zruuFTQV>?wxj84Y?U>ouof z3Fp@}WmQpGC!MRgX<@XJn`IvqZpJW$utuPwvak$?sb5%}#a&QP);lfewTLDQuY#cL z6_=($jPl7}t=KvR4!OsJvGQ8rwz&$srr*1YA*>i#ROwf7*Qdl1!$bX3bItxCEtp?y zFSKmw(I|~N_o1)jdW)pNy%pa%y4}B;_~!q>5**1Qf^xi{i4AGC>AT8bg+F%pK8Sv5 zoYVfaCU)geo1v1LJK|R#$Nf)x<)GpHzb5{VVwtaCX3MU zn{F~88`Y?&9BhVs6lY23Du{VFp!siOR=3#sW+byT)b;QE^tCZb7@B+8e_72kGsncE zeONQ9Y#}Q0RIj*_Pd$&qmBGOf?etc~59YZH}7&CPmo^N@>7^7C$s zQ5EI*kyIq}OE!~@Ezz_A8DgrVT`9ETjjZK*uc(DErA6e@)a=uax*u5iQ`xPcR@J7+ zsIjmilRnZGw$Lv>Od^J3CWRRT5iM5{Htj^%r+iE3M7ypmp5>8IEFeF9Iz0M!32~Xr%ti61z2CkmWs1V=UXfU zmxe@SJ%m)Z)|)kT5n-SeTPFmUy|NcmWBdn}?7Shija{M%U;pP@QVe6QD>2~)JKE`8 zDl@4pI~n53_7m9R#F!duhkry(nunoUCKWDgl>kFQ58j)6KVl+>I8fP}D=JK7t%>Ym z(3Z|?A!p}1nvSz6UaKGSvz4oBg7zGX*@hmVg&lA8?B{1IZ1 zz`xrjz&P{q4q1oN$#kMilP(zTNq~V`qV-NfBS*ulA}ym1VCmF^7z*6+)s7g|3kd75 z_OOsKHMmi}C&s^x0^vPAXe}N&3cbOOcFlJ2x(E>mrfdIjUe~>Pb*RQw<78Up8DW`;^-|Cxgt;3pbKm-87dS*L zEaV$R>z(G4PZK2jr`SaB_;>8IA5)D8xe@=B7yEkwj5A@@jvtd-XZ<_X0vHme+MtRX zxz^GpA|~D1g|_Pw_LF6wTRKyNljF}8ul!CHJE7UEyZEkf)%Y4%!rL#zLb0g};(_VG zm9t|AcZ1p?ltb88$lZ4*RU~g^BoQsyp|AGtzU8aaKJlSADf06UNub$2-!!BKyPL2+ z^tGsx2&GUIx}Qr%0cQ_o-I7H$C`hce{b8ryU(Ms|PWY*h1^(`Kc=S z7B8Rw!RL~cat*=r^^!Z7$i1L>2qgl`*9YA1+vG3XQGuLQ-g#Y;s%qle)d-`xkuT8Z zpf6*iXYO9@_Gg{mIt$m^cZDvI&%m-AO+KcZKh^)2o+8v@FE&7`i2}%pN=>E9f2d+aEl8Skz%QyrfxvD(!!p72;}pVbtVlw|_c) zBS6XhYPpVeFM*}8~w9WF+n~r_0{%=>u}=+AZa?;2;-ln`Z;+AZ=5S_!A-A)6r940Us3~T!wYweTitKPkjWcloEdx(+FpV<%&RD^R{ps zK{pTTlLH-GNKt^}Dk1|A`>B78LqaIf9ZE3nSX2`&y8i&?*Uf;a6y%yLBDafmY$OCn zWDPaV;kX2L)$1uH-l0qVL`lO^`SubCgG`d@iPT{?CN`)F!;9py=d^7A_^!1ZbU_k= zbsY@U6saNn_mp<-M6=hM$w*MPi&bL%BA5X?3BZ~v$mzl}dIDGNTae(p!_vSo_zTCR zQq`X@YQ&}K>-3b9vPq{@m#rhRz>w zjpKBqVF6kfkw~r0`)hQojs%!YdCFD~L!qq32G<3vpP4BNp*Ymkz7i9`_Q@yud`)~K zEMF-jkuKJ@Y$LbeCax0BBxpKPz`7-4oNrov${vJxqF;;Ws z%EM~n%K!n}huX{^_}8tDfG8)eA=~5$Oy9D}-ffIcli*vzo`ZjL=!bB=l~u0s zzeGR}fe5Na(j|4JI&2`zR)i&iGVJ}IZEmt@@hQ=1?v;v72OKzZL6QH*84&+M-b`~^ zl9`x!Qnak-HY~RXbB~^(WSwNkt;U0=2H#us{=GL&+DwxiYOl=YJmxridKLzoLEW3! zP_krsMtk2x=o46ezDG`OH`Vf(R|tjPxIX9~IiLxwhIG{*ax0sv)+y`(;e?p{_l|Z_ z9&c$-X~tTmr>1PH=Zx&*pA#>3KFxLUO>E@Gx!DdcBxQcey_o*%)^{7*CQ;O5oc2;o zElHY|qn>wIyUj!SH}Zm8rc1?MK%fij?*f;DlC9GjtXdm@b7(XIHwZ9OBipRqp2 zjp6>f`2)WNSLo&Hy~i88msrkDGSZ)g!KIczy7s*pEU-|ZbhK)F?9M?3CsVD>Ha0s{ ze>455r>J`IcskMQqNtGIG|}v-U)Iknhu>a@y2SYKG@O;*lEg?)oD$jZN$oEFP>qW_ zle8_q*IvsZTNVDv#&#WFfzl0K{h4^5eU6$Wy4HzRe(y76NTAaDKQGesO}(>c9UKN9 zH;QFTH#7uh(s@-(?e6qSSbKNg#M)U2zdZN94WU1$?A#N;@e&SmUGITDP49Hz z-`tJ7p-2t=Vi~lU%0t)pr$UeE{_}|A@07wHU=Hj>-;D zO;Q1()AI->Nv}M(^YGo|ZYIxWn<1>ufP3F9-)rRTWcn0d6@&vyK&x3oA(CbBfX&&n zm}OPqO81p^DpuKv>E@v73{B{(#PbWwJ%{(%UZ&%o#cjIDIy_>sP7^Pge$KK-D?1+R z9U|@4tm}HSeLr_KqH21adB2JP&hR%LSN{POyzcnf5GOHsW_s&oZdcO~4ngWwv9pxv zxs!(;qL6lv_I3*r00*lI{1t7<$g;jJ@y;!^=Zb}=RKfcg<#@-OBJp`APbw*_t;T7Z zfAw?7(CRGM!Mm)mG5l8#8HPGt?(rYeS@`6}2&&#ckie2}fGTJUnX~v{!B!)9)IYtG z)1;JADu_wMqINLr{_lGlen}dz;Hwas=5)P{m1|aQsF+HaugoJ5lFHZQ=HdBHWl*wR zjlzXUothHPi03ysy1u?yBIiv{TepP&;2t@1%t~-k zT9ofjMOy{V_z2`!w%UmsRf$iyg%81V(2Xjfm7Z|wDC3})qO9e%cdC4#>! zvm-*341g<)vd(bp!jI}t3CGQZJ^TK|r#{-G%AYcw{wxwyKz5pkKx3i~H5+Z^;$!HofJzFQ~+aI9+4LAnq>s<4A3I`Zi zSjXwqPVi5}(Qu8Sh`{qWyop29PeJ(T^hznweLFAg3wuGS_D$z=nh(i zxv^rDeClQA9f~^Rr(WV8*FkyuG$#ZrN*-bX0_<2_ncMSWll7Z6ph^TAlZ3|&(gt0H zvytZCZjDwxbzPLP*Ruu!9pYup!A303s{Slfsa&CuE?HFr(yF0C~QM8HgUwbnnRf;9O-}75i@KeKllw z-&+1>8>I%@8X>wvA&QPlT0cUjc{PrSlKXC@&6E*?n2sJD7+9h2hYvLStVK2NzKxG?5ndP|zb)6zqRMM-{2I0UiRWQ30B{0F_9kzVpg!Q9FoM(=x+6J^r( zbPKg6h@KOiaI9SY*lcnNa!wmqBB`@i)|*K;U(|=wp32bg!u<1I&f2$4n{tNJTIU z81h4{b{0kIwGyFocb@&4BJ~{>uP=&Z?En-j3JHVKulkl5R}jv#1KyOb5aAQK5lwG> zpX};w&_6BOKFAcO)k6;oF68ChPm7A?iZ`d#rN0;8t{Y1k zS#u~RhI()}j_sr64$v%xL=}&uoj3UHV!y7)bQb;~ewUG1QK9D>cgKVvpg$svEwx!} ze|&Fj2kDzSn=-2tPWJkb*c_5X$0+3f8JG0Q(Lt zS-M0x+bhAv)TUT+anH-u6C&t= zg9nh+EiukF{6tMIe~L2={8;yWxkO<8#c#60_2GCXi=O=7IS);^)RJN8jdVGLTA#nM zr8QG3WEDfopwpgyydV>S$pfM4^7R@*!mNcSE)ANh?UkdMnBiMZB{aIpMDR4U=44lB zKCiKNKh#}9OA3069kXtAfi65vFg_CUD}V2inLmGnao%m_v9N2B9yRzu!Q+P*Q{t3U zJEaW{kBF8($H#G>6S&Um3;He^o}|S~q1mQ>|7?*(+sr*frUrXkE9neN`;5EYIjijo zv*jV5{nOLZU6j}h{}5mcJXzi>Tx=;3>|#slSxT$ezI+nubC`B9wYW6TY4RXL_Gr^n zu{0Uaij{Y3w^1m}i}$?D%XfQTWqp?Wr9E~Y*^?k`Vkk6@0n5VKIq03PDxDm@bR4<; z3KgAQS6tE>$=RMjOAML3!vC>k@Tn4FRA%+wPwWVC7k*zCkPT|tCEiwVSMB=`#n+|4 z%X8( zDD@90OHoQym##{#RXAvUYzkO^89X>%;q&1J6BT5aRO%mSmAWEDf!!6<@ZK}0fhiCo z?8)FL)==M%ns|hVPrnA9EdTLOLQ2Ld5=ie=u)y~2yen)HMvD&&LM9xCUudwpoyjmfwFuYG`y@vfdzslx)XUPY?XbaO5S2iy`$Em3u57o~kiV=cd;}7fe`P zD}MZ*-P0x&SIaI0< zoQ14UOj&$w@&DQrVXLgylo)S^R+pZOH!$5EU-3+?wyf}&+AdW}TxD2*q6GG){3B0e zm@zp}5_s;(mgTA#Jz8L4B%BNFuo%$CgP8P7XIc6(i7UEtc50{ZZ`7vhr#V{nQAm^_ zvuH{BEn`{yjg%f!yQ)2A8hzWhHKYU2%TguHs0V+xJ&bm@+OZoooIqM3YB6T1lb(i# zduaHbq3cVu^R11wS9MdR2F+NFZ*r)WP52ZQa{9JgS#13oe8_RAJ%m*cpqS-3kf&5h z8S>ZK(nuq>kDAwL?BGl9L8&1o>o(MBg=We;xLG`nO#)mT`+Vy?4QXyr%zrQvekeUy z>L9D#R}sl`Qo3V$lz1n)=ppNd$J}SuYZDtYHIea02UXG0Q*~AO@p{_5fr^#v@0Hz( zGa~Pga|BG>m8s=Gc$&S-C=jWAurg+Wg73uf%Ok|vYt2UW#e=S!*KT=s-CTrxyO~~6 z=Sq3Y_g@^_fcI7MAcYHe(-U@A_r9x{;EY_aoeAR{8n9AY?w zJ#LIF(2&wvMP39Rd6=O0GNuy{zcV%>GW|HLc8h)vQ|CGj!n+C?xAviAe>wbRh!MOb zQud)aUn8nLXl#FRba!R08iMMqt8gkF9R%dxP~GxlorLUMZ0l}B?&u#=Um=XFqEO5? z|8PYeD#Vxd-HD*`fOGzlGq&Dxz1_(@`sH2~YJ+&BNR}qX(V{`;V=>GUf^UFY8 zhwsxPNa$U7mqBq-)bN&Rv+-dPX2%7k?UL6o`LVmo6L)Ft2A;?e#?gV5L#!E+rqiFx zurlQQs;FvH5+RV%{ib;2Lm)4l5{f5r>G2SiJ4{LP!i5Q~_>aL56-{MbdEuJmtN4$Z zd9E5lp~As|!uR&1hZT~~P5)4cdgapp(H8`GWYkeWCQAv5a$kTC_~Df(K;Y<*3043? z*@LLz%1L(#0g4HceZ%~n8&5*HJUPfN*=SR1Ax3shun2aM2&>pDY?M#jGp_68tZ1yN zU|)Z!&BWh+uvzY}7d%9ad?3Kdu;O&GNs)A*^(89?;ZKuhq)r0(ScGMV3Q~YiI#NFK zp7YnqbfctCI7NrIe%Z{YbY`ap6RmmupZvWW2L#tXJd=NRI-qh0!cVu@xn5mf>|2s5 z%&&5$|J?HPnFVypqnn;zCjM+!={Rdq6*pHl{t|>|&!V`eY{+o=b%QQ_`H8U6#e=`J zY;K#H{%Afl7XI;w0cc{U~n49DeF_L+r?z(!SJ+SBM2SUaP0}dr5nv3x3(vt zW4#49*W}kC3ZuHYM0g{{$67^{D1Ydy|l7hd7wr&rq?b~#AHW5e$ ze2Rcdc;^LWeveE#$QK>#(i`YYK!S7MA(#8m4uAvB+J%l6gz{kpg`AeOg&-CiSCg%; zQ@ZB3cX5XQo)WFR*NN_TAIhb<9te_BW#^f|HmLUdFLJ2XXMCPqN{~O5ILL52El=n+ zseGK^i>@hsBKDw^O9dtr?XHM)M8xh%fzr1t?6XH=St(758mwT@s~;P0)Yo=G-8cEl zGQG`9V+Y^+eq{OaM07s?OO-f22SHY&2pal2krOvl-}AhVZuqi2QRp@wy;7we>noIr z&w^`Ns1YO-a5rMLT7atx;oVhRk!-zcE z-*S*C`-!wZR||hQvR_!6T8XfliX>q%sErLp!feNr!#+j}S~ICH)S#aUhkGKsar?YD zlcuRz=BBjP>X`x@O5G&2>pRN0)8R~S5so~0vwwY!zX{cR5IiLhWTjHvpnxC3F&}F_ z8UCF80{5_BT+hDn;r7t9k_em3GMqU{k!Rx`JechXY-ozx&Dy`R#uV$Go4Fx=3s|7N ze(h%FwLh0r%dOVuLM6>_YBom3Gx7{LY8$wBB}DTfea3jIPG|bDfLGGX&Og37$ySG*eY-6q(*QPA*`Sdx3{{d z{ShewB=nq2>u;E+ z(^J3dOg4PZO>ZeHMZ#V$so_>#%ewCa>*KL>Q@{()AarKKLwcUtinY*&wU5LT=dbtj z3ZD2Mott*%cHlt3q(Wk*H;cNehWF2f@>Vuga-!#WYOSop;%GE{XHBkq5qU5*cWwW} zAX&izHxB({>CUXu82G>7(%zYEYSy%Hj^bWGQw!DY%Nkls2@>7+s9Ize7V<8)3rUC? zhlVzXUjOA^A!WLH$G3>(_Rf(%dvJ5;jOMF42~*rB|Du}yJA11qUrqJ&U)9w^k+!k! zM#RiA-PbU{rY4|my;6s$)n4ZJQ5eC0B!DQ4*#G}TNDpDgkY&1)0f@%yILoopH4_5{ z`i#_69t;KXy@H91^`BA2{vv|B@RZrou(lRr`DhBfxIsw_J_xI%I5ka0w3zo7iV4^@ zReACA9$z07zH5_fP9)Pvf`|Al3P^!ZGBjWoWk~BK7>p6uatjbq+T#IT=Jzyk(v_~v zsma?+P`ck+6f%aA5bkIuF7`JNV~j?`&3xb*q3Z4-a; zBXKj*OI_j{WbM0lPdW(!Mq--d*oH!RLkfnw;4YcQ;X0G~jsCSFlXKa4OCC1;yGI4t zm6ribBDnpl?^WcP{h*ibN$`7m`*!u)TZ0IZZa2e6QwlzVc4{wc-N(b*3Z|1Rfy5#+ zML4L|YWuugYk89lfKgMoEEs<8P>MaB2f{h;i%hL~Mf@?mJMmCgkztMSW2HPMFi`Ld z?vPVqzr#N{E~6}R4`@pUsV1lwHQC7@65asR-@kap7NteOcoJ{`H#!|@wX5S%E!7buXyr9l|mB&tRpZp7)~mAS1?GoqPgH9X|PX_=wGj%rz8NbB-@4Sl%}SHX4V-( zajASu0p!!z2+Iv+A$o%JZ>4c+aVzj1Lw_&>s)v>yP*tBn65ai?vzJZZx{pzh<4 zoS<9mSVF0jL+U~w?j=^1@Hn+(n^qb}$u2nP-~pQ(%EUbft=4mZ@CZJJZWTpXTAZ>; z!6v(M3f}wg7P?2-$Y*LndJogD3TVdIzF4V7u~MT z*Sd-mLm6g!n1OpvHG<2sX#uZvZVhelXO474qU1g{F{YeWi9m@)Is1fclZZ^Vblw_o3EY zj||(jZz5REb?*6o{^p;a7M6SFRYKk{+Y%hkx$KSKb0Xm`RYZF7Uan9eTLwY+?eLZX zWD^VF_x<;@3vjqgDC@PrlfBCXRS*NhLlBUl-vVC(3Bf<1_>ynpWAguTqJyaLZ+D_u z86;#6U?78cgR+$Iv4w@sHmPo26(wI>$d}Ijqz=g4DBs=X*6HyI_*!R%@Z>A&*56=U zWT(#!di>(zwrAY7{gL-Zm;Jqxv0LDHNp`Wyo7rkJU8w*H@zz2uepc|^X7>*q35|XK ziql?*9}Y$y$P?57p=3_K0#!!OkX>R;27kG6W8h%v^}~Yqvw7wgi0BZ+_5fvsAqALY z5>8s0b{p`E^O+cC8V>GLIS+x9>5aQD-Mk3#BNjtp$>ebO$|GWe`M=5Ay-Qh|bT6{| zY9c;6-`$rw4TxR2mI1TH8CK5WEKXWz(ef0Je2A=qj=PCDdwR=8tFp)meQ#2ZXz({Q znOd8GbULma&SoX^c2qv5D@cFO_Y z#&RugJVa(DtRPeyplj=!vl$g>UEc!+Eb3mZe@HTI>_6#J|Xh}iZ0%>Lci$> zYG!;xLdlotG@d2bbS0(+=g&nn+5FTK+1t1(e40u<=>NufilU&vqOB4&AtpFZF(-VJ z3V@=t(g^R9fhj)jb1&6>obr_rRx2h}%Vv z?abt1x<$kqdR(G?zs-IC36;3mVM;>rhH1+TNJz6>*!ezoY@M5E^Cq%O-nXKu!_g6Y zax+7dm?XZCN|la3lIKbucT^Fr?DDFQ=uMtHAQ8M$Bx{u~KWOcID&GYWTA;*k7?| zW(xsCAlSDlkP@4DO4*53`gM&M19hWg2;3jFIBQiEEF_=-042gw5kk2mh)+TB)WZmI zOw~~Kb+dZtA2|(P? zP{8wO(13V|M;#47p&NX32}QtuilR=cm&WDy1mN!+#Rjtf01`;*6U0z(p8H=5?jye3 z9;64*2tPDA#)!&{8gCghJC*^=hqVK^FQ!6mHa)scC`ePti`+WO>E{aS>05D8~}>l;e?A*8M1)w z2hA>YuQ~a%dIa^`ROijblTxl^?VTTus#4@7>HdvqH!I|`#J2yg$wwin;~|`+t1MVU5r-T$E}+b#vh_h@M@XxbRc-h zxI^bzC+$Vug>0v$TeIdjTt&Hdxw~o{Meu!H#!rnKwgz=pV#`)oVy7 zrQf;)G?kGb$$E+F7Ia0$ z$^Q(m!?#Q!$FSvX1mWSpUazsQffeAbM4)E$jvGIWRy5OIj$HG&L}Ih zH(KmC%x91gOBRRmu7VnzK8a)n^veehz+d^S?mggo3UdFoX zuK1jUq>WI)F(zUJ^jRni4ijMpaLk0D*DC^;-+){(7lyV#88M*JO&y1h2-P_sEJX}p za5}PF!dFB!LOtYY^TOV{wOJ3cjkbqa+Zh|@Krj*K{Dp7G61b%D=AQYrTPgg-UhtY7 z4KbS=Fin<*E>BJ#doPAPE%nzfq+qez-J zQR@^5c+?V|9#oqRLnX5e)SvXnpQVv=X-}6f5tD=)5$(dOKBe8Bf4R3@*@wD0T@W6; zWS~UtF0rlyF+kV^P>78{2_clCBT%rCNEC>rke=<8$yd2Ip9DWB`4_?nl!VCJD+|n(sQ|IbYm22+lKd)9?_k6SBN!H1aK9`p^$?ksC!uhWCyk?y*8eI``eM1&=ku#abtdSv zx%^*`9pQa#6v{xkC4C04Reo)xF181ocOvLgni|8ET`?6QqPqSB7z}i1L>izXEM+yscgecH5MzWn#E?u?v__T0$~VQ>4k2l+_a1M+sy^|NWL*D6mc zZ>6=%Jcb+__yG^6atA_D1(b5)`4Rmu3y3_*+(@RBK1XC2Jb4{I>6#@Gw;9LU)>F)dk}@6Q#6h7Ki+D=6_{<4sb*C zn&H&AZ*7Y{STNMFNe!L*9>$jxom&yte_|6{*F6hgH$PxGZ8AgGt+l4rM%R9^LXSw4 zCz(E*3|*?78$JjcAk5`o-DMX+2ao($tx&wVf2Y&ze&D$-vN1&}T!(9v;9(6*+Vf}F`me4<<9m}Vw+!oZ zVdACoF-GC#X16t2a*IGf7B>Eg|H_d@tRwYI`pF6fI<(*KoRPkLwvWZ(qT&Nq?$;U; ztfG}5f-Zl)Ac9g~XIs#IkSzIElK#ZOX=m--D3a}#dq=$5>Ef;;2QUI~@X%l3j>F@9s}-Vomq`uMpQl~;8u(rIoNhmkigc=mJSLjUig%KA zpV+)IKRgY(53r&m+55R#AS=OeK+wOen%`9n6}skI+npc#?`W(ID3S=+n4l#sd(7oH znn%+_%PxkUQAGdrV9Ai{NYFTSKxj?)Z%uv*N(5x(E`2?eKs_Za7Sjjxe+`yF>XT`&&B8Bo9{8n zTGk9XLMJ~j7ltq0iBH^fLBlS>)ZlFJt4B&%QPw*yO@GeJ(g)^fvKHklCnU4$t&AlW zL_OMt>)V3gtDW(iY@nXvYr>;YKBK^6yij@hLVS*U;IEeddH|!Ptrh8vDH~X}Ic?XG@sogm1_2r6c8qSMmzN0coOE7pLVA zY~VXIj=Oz20AdUQ~RSU+~~~#OFGEv-Ct({S}H5g+-Em==4oGOUbtURb9nZ zXJ0u44JAe;@N`681Ja_j6l)@+D7qbAIH)>uB-Xvr1YQvWX{hQvf+;_b{9jDY6YT&1 literal 0 HcmV?d00001 diff --git a/pom.xml b/pom.xml index eeb658d..dce2f5d 100644 --- a/pom.xml +++ b/pom.xml @@ -1,47 +1,207 @@ - + 4.0.0 - org.mender + com.github.alexisjehan dsv-mender - 1.01 + 1.0.0-SNAPSHOT + jar + + DSV Mender + A Java 11+ library to fix malformed DSV (Delimiter-Separated Values) data automatically. + https://github.com/alexisjehan/dsv-mender + 2017 + + + MIT License + https://opensource.org/licenses/MIT + repo + + + + Alexis Jehan + https://github.com/alexisjehan + + + + alexisjehan + Alexis Jehan + alexis.jehan2@gmail.com + Alexis Jehan + https://github.com/alexisjehan + + + + + scm:git:git@github.com:alexisjehan/dsv-mender.git + scm:git:git@github.com:alexisjehan/dsv-mender.git + https://github.com/alexisjehan/dsv-mender + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + Github + https://github.com/alexisjehan/dsv-mender/issues + + + Travis CI + https://travis-ci.org/alexisjehan/dsv-mender + + + + 11 + 11 + 11 + true + true + false + UTF-8 + UTF-8 + - org.apache.commons - commons-lang3 - 3.6 + com.github.alexisjehan + javanilla + 1.4.1 - - junit - junit - 4.12 + org.junit.jupiter + junit-jupiter + 5.4.2 test org.assertj assertj-core - 3.8.0 + 3.12.2 test + + + ${project.basedir} + + LICENSE.txt + + + org.apache.maven.plugins maven-compiler-plugin - 3.7.0 + 3.8.0 + + + -Xlint:unchecked + + + **/benchmarks/** + **/examples/** + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.22.1 + + + org.apache.maven.plugins + maven-source-plugin + 3.0.1 + + + sources-jar + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.0 + + benchmarks + examples + -html5 + + + + javadoc-jar + + jar + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + com.mycila + license-maven-plugin + 3.0 + +
LICENSE.txt
+ + src/**/*.java + + + SLASHSTAR_STYLE + +
+ + + default-check + + check + + + +
+ + org.jacoco + jacoco-maven-plugin + 0.8.3 - 1.8 - 1.8 - UTF-8 - true + + **/benchmarks/** + **/examples/** + + + + default-prepare-agent + + prepare-agent + + +
diff --git a/src/main/java/com/github/alexisjehan/mender/api/MendCandidate.java b/src/main/java/com/github/alexisjehan/mender/api/MendCandidate.java new file mode 100644 index 0000000..5e0e519 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/MendCandidate.java @@ -0,0 +1,46 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api; + +/** + *

A candidate of a mending operation composed of a value and its score.

+ * @param the value's type + * @since 1.0.0 + */ +public interface MendCandidate { + + /** + *

Get the fixed value.

+ * @return the fixed value + * @since 1.0.0 + */ + V getValue(); + + /** + *

Get the score.

+ * @return the score + * @since 1.0.0 + */ + double getScore(); +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/MendException.java b/src/main/java/com/github/alexisjehan/mender/api/MendException.java new file mode 100644 index 0000000..d394277 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/MendException.java @@ -0,0 +1,50 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api; + +import com.github.alexisjehan.javanilla.misc.quality.Ensure; + +/** + *

An unchecked {@link Exception} that can occur with {@link Mender#mend(Object)}.

+ *

Note: This class is serializable.

+ * @since 1.0.0 + */ +public final class MendException extends RuntimeException { + + /** + *

Serial version unique ID.

+ * @since 1.0.0 + */ + private static final long serialVersionUID = -2548765841738762668L; + + /** + *

Constructor with a message.

+ * @param message the message + * @throws NullPointerException if the message is {@code null} + * @since 1.0.0 + */ + public MendException(final String message) { + super(Ensure.notNull("message", message)); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/MendResult.java b/src/main/java/com/github/alexisjehan/mender/api/MendResult.java new file mode 100644 index 0000000..ee92cfd --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/MendResult.java @@ -0,0 +1,56 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api; + +import java.util.Set; + +/** + *

A result of a mending operation containing all candidates and the best one.

+ * @param the value's type + * @param candidates' type + * @since 1.0.0 + */ +public interface MendResult> { + + /** + *

Get the initial value.

+ * @return the initial value + * @since 1.0.0 + */ + V getValue(); + + /** + *

Get the {@link Set} of all candidates.

+ * @return the {@link Set} of all candidates + * @since 1.0.0 + */ + Set getCandidates(); + + /** + *

Get the best candidate.

+ * @return the best candidate + * @since 1.0.0 + */ + C getBestCandidate(); +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/Mender.java b/src/main/java/com/github/alexisjehan/mender/api/Mender.java new file mode 100644 index 0000000..3f88553 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/Mender.java @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api; + +import com.github.alexisjehan.mender.api.evaluators.Evaluator; + +import java.util.Optional; + +/** + *

Interface for objects that can mend a value. Fixed values are generated and then scored by some + * {@link Evaluator}s and the best one is returned.

+ * @param the value's type + * @param the last result's type + * @since 1.0.0 + */ +public interface Mender>> { + + /** + *

Mend the given value if needed.

+ * @param value the value to mend + * @return the best fixed value + * @throws MendException might occurs if mending the value is not possible + * @since 1.0.0 + */ + V mend(final V value); + + /** + *

Optionally get the {@link MendResult} of the last {@link #mend(Object)} call.

+ * @return an {@link Optional} of the last {@link MendResult} + * @since 1.0.0 + */ + Optional getLastResult(); +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluator.java b/src/main/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluator.java new file mode 100644 index 0000000..c0cf448 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluator.java @@ -0,0 +1,74 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api.evaluators; + +import com.github.alexisjehan.javanilla.misc.quality.Ensure; + +import java.util.function.Predicate; + +/** + *

A binary {@link Evaluator} that is based on values validity.

+ * @param the value's type + * @since 1.0.0 + */ +public final class ConstraintEvaluator implements Evaluator { + + /** + *

Validator {@link Predicate}.

+ * @since 1.0.0 + */ + private final Predicate validator; + + /** + *

Constructor with a validator {@link Predicate}.

+ * @param validator the validator {@link Predicate} + * @throws NullPointerException if the validator {@link Predicate} is {@code null} + * @since 1.0.0 + */ + public ConstraintEvaluator(final Predicate validator) { + Ensure.notNull("validator", validator); + this.validator = validator; + } + + /** + *

Check that the given value is valid.

+ * @param value the value to check + * @return {@code true} if the value is valid + * @since 1.0.0 + */ + public boolean isValid(final V value) { + return validator.test(value); + } + + /** + *

Evaluate the given value based on its validity.

+ * @param value the value to evaluate + * @return {@code 1} if the value is valid, {@code NaN} otherwise + * @since 1.0.0 + */ + @Override + public double evaluate(final V value) { + return validator.test(value) ? 1.0d : Double.NaN; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluator.java b/src/main/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluator.java new file mode 100644 index 0000000..1bdec47 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluator.java @@ -0,0 +1,85 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api.evaluators; + +import com.github.alexisjehan.javanilla.misc.quality.Ensure; +import com.github.alexisjehan.javanilla.util.collection.bags.Bag; +import com.github.alexisjehan.javanilla.util.collection.bags.MapBag; + +import java.util.function.Function; + +/** + *

An empiric {@link Evaluator} that can be fitted to evaluate transformed values based on their frequency.

+ * @param the value's type + * @since 1.0.0 + */ +public final class EstimationEvaluator implements Evaluator { + + /** + *

Transformer {@link Function}.

+ * @since 1.0.0 + */ + private final Function transformer; + + /** + *

{@link Bag} to count transformed values.

+ * @since 1.0.0 + */ + private final Bag bag = new MapBag<>(); + + /** + *

Constructor with a transformer {@link Function}.

+ * @param transformer the transformer {@link Function} + * @throws NullPointerException if the transformer {@link Function} is {@code null} + * @since 1.0.0 + */ + public EstimationEvaluator(final Function transformer) { + Ensure.notNull("transformer", transformer); + this.transformer = transformer; + } + + /** + *

Adjust the frequency of the given value after being transformed.

+ * @param value the value to fit + * @since 1.0.0 + */ + public void fit(final V value) { + bag.add(transformer.apply(value)); + } + + /** + *

Evaluate the given value after being transformed, based on the frequency among all of them.

+ * @param value the value to evaluate + * @return a score between {@code 0} and {@code 1} + * @since 1.0.0 + */ + @Override + public double evaluate(final V value) { + final var size = bag.size(); + if (0L == size) { + return Double.NaN; + } + return (double) bag.count(transformer.apply(value)) / size; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/evaluators/Evaluator.java b/src/main/java/com/github/alexisjehan/mender/api/evaluators/Evaluator.java new file mode 100644 index 0000000..c02aff9 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/evaluators/Evaluator.java @@ -0,0 +1,41 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api.evaluators; + +/** + *

Interface for a component able to evaluate values.

+ * @param the value's type + * @since 1.0.0 + */ +@FunctionalInterface +public interface Evaluator { + + /** + *

Evaluate the given value by returning its score.

+ * @param value the value to evaluate + * @return the value's score + * @since 1.0.0 + */ + double evaluate(final V value); +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/evaluators/package-info.java b/src/main/java/com/github/alexisjehan/mender/api/evaluators/package-info.java new file mode 100644 index 0000000..c897c79 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/evaluators/package-info.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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. + */ +/** + *

{@link com.github.alexisjehan.mender.api.evaluators.Evaluator} implementations.

+ * @since 1.0.0 + */ +package com.github.alexisjehan.mender.api.evaluators; \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/api/package-info.java b/src/main/java/com/github/alexisjehan/mender/api/package-info.java new file mode 100644 index 0000000..d645251 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/api/package-info.java @@ -0,0 +1,28 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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. + */ +/** + *

{@link com.github.alexisjehan.mender.api.Mender} interfaces and components.

+ * @since 1.0.0 + */ +package com.github.alexisjehan.mender.api; \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendCandidate.java b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendCandidate.java new file mode 100644 index 0000000..653c6c6 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendCandidate.java @@ -0,0 +1,107 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.mender.api.MendCandidate; +import com.github.alexisjehan.javanilla.misc.quality.Ensure; +import com.github.alexisjehan.javanilla.misc.quality.Equals; +import com.github.alexisjehan.javanilla.misc.quality.HashCode; +import com.github.alexisjehan.javanilla.misc.quality.ToString; +import com.github.alexisjehan.javanilla.misc.tuples.Pair; + +/** + *

An immutable {@link MendCandidate} implementation to work with {@link DsvMender}.

+ *

Note: This class implements its own {@link #equals(Object)}, {@link #hashCode()} and {@link #toString()} + * methods.

+ * @since 1.0.0 + */ +public final class DsvMendCandidate implements MendCandidate { + + /** + *

Fixed value.

+ * @since 1.0.0 + */ + private final String[] value; + + /** + *

Value's score.

+ * @since 1.0.0 + */ + private final double score; + + /** + *

Constructor with a fixed value and its score.

+ * @param value the fixed value + * @param score the score + * @throws NullPointerException if the fixed value is {@code null} + * @throws IllegalArgumentException if the fixed value is empty or if the score is lower than {@code 0} + * @since 1.0.0 + */ + DsvMendCandidate(final String[] value, final double score) { + Ensure.notNullAndNotEmpty("value", value); + Ensure.greaterThanOrEqualTo("score", score, 0.0d); + this.value = value.clone(); + this.score = score; + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DsvMendCandidate)) { + return false; + } + final var other = (DsvMendCandidate) object; + return Equals.equals(value, other.value) + && Equals.equals(score, other.score); + } + + @Override + public int hashCode() { + return HashCode.of( + HashCode.hashCode(value), + HashCode.hashCode(score) + ); + } + + @Override + public String toString() { + return ToString.of( + this, + Pair.of("value", ToString.toString(value)), + Pair.of("score", ToString.toString(score)) + ); + } + + @Override + public String[] getValue() { + return value.clone(); + } + + @Override + public double getScore() { + return score; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendResult.java b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendResult.java new file mode 100644 index 0000000..73717c6 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMendResult.java @@ -0,0 +1,128 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.mender.api.MendResult; +import com.github.alexisjehan.javanilla.misc.quality.Ensure; +import com.github.alexisjehan.javanilla.misc.quality.Equals; +import com.github.alexisjehan.javanilla.misc.quality.HashCode; +import com.github.alexisjehan.javanilla.misc.quality.ToString; +import com.github.alexisjehan.javanilla.misc.tuples.Pair; + +import java.util.Set; + +/** + *

An immutable {@link MendResult} implementation to work with {@link DsvMender}.

+ *

Note: This class implements its own {@link #equals(Object)}, {@link #hashCode()} and {@link #toString()} + * methods.

+ * @since 1.0.0 + */ +public final class DsvMendResult implements MendResult { + + /** + *

Initial value.

+ * @since 1.0.0 + */ + private final String[] value; + + /** + *

{@link Set} of all candidates.

+ * @since 1.0.0 + */ + private final Set candidates; + + /** + *

Best candidate.

+ * @since 1.0.0 + */ + private final DsvMendCandidate bestCandidate; + + /** + *

Constructor with an initial value, its candidates and the best one.

+ * @param value the initial value + * @param candidates the {@link Set} of all candidates + * @param bestCandidate the best candidate + * @throws NullPointerException if the initial value, the {@link Set} of all candidates or any of them or the best + * candidate is {@code null} + * @throws IllegalArgumentException if the initial value or the {@link Set} of candidates is empty + * @since 1.0.0 + */ + DsvMendResult(final String[] value, final Set candidates, final DsvMendCandidate bestCandidate) { + Ensure.notNullAndNotEmpty("value", value); + Ensure.notNullAndNotNullElements("candidates", candidates); + Ensure.notNullAndNotEmpty("candidates", candidates); + Ensure.notNull("bestCandidate", bestCandidate); + this.value = value.clone(); + this.candidates = Set.copyOf(candidates); + this.bestCandidate = bestCandidate; + } + + @Override + public boolean equals(final Object object) { + if (this == object) { + return true; + } + if (!(object instanceof DsvMendResult)) { + return false; + } + final var other = (DsvMendResult) object; + return Equals.equals(value, other.value) + && Equals.equals(candidates, other.candidates) + && Equals.equals(bestCandidate, other.bestCandidate); + } + + @Override + public int hashCode() { + return HashCode.of( + HashCode.hashCode(value), + HashCode.hashCode(candidates), + HashCode.hashCode(bestCandidate) + ); + } + + @Override + public String toString() { + return ToString.of( + this, + Pair.of("value", ToString.toString(value)), + Pair.of("candidates", ToString.toString(candidates.size())), + Pair.of("bestCandidate", ToString.toString(bestCandidate)) + ); + } + + @Override + public String[] getValue() { + return value.clone(); + } + + @Override + public Set getCandidates() { + return candidates; + } + + @Override + public DsvMendCandidate getBestCandidate() { + return bestCandidate; + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/dsv/DsvMender.java b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMender.java new file mode 100644 index 0000000..5af5d50 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/dsv/DsvMender.java @@ -0,0 +1,626 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.mender.api.MendException; +import com.github.alexisjehan.mender.api.Mender; +import com.github.alexisjehan.mender.api.evaluators.ConstraintEvaluator; +import com.github.alexisjehan.mender.api.evaluators.EstimationEvaluator; +import com.github.alexisjehan.javanilla.lang.Strings; +import com.github.alexisjehan.javanilla.lang.array.ObjectArrays; +import com.github.alexisjehan.javanilla.misc.quality.Ensure; +import com.github.alexisjehan.javanilla.misc.quality.ToString; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; + +/** + *

{@link Mender} able to mend invalid DSV (Delimiter-Separated Values) rows based on valid ones.

+ * @since 1.0.0 + */ +public final class DsvMender implements Mender { + + /** + *

{@link Builder}'s step to set the delimiter.

+ * @since 1.0.0 + */ + @FunctionalInterface + public interface DelimiterStep { + + /** + *

Set the delimiter with the given {@code char}.

+ * @param delimiter the {@code char} delimiter + * @return the current {@link Builder} at the next step + * @since 1.0.0 + */ + default LengthStep withDelimiter(final char delimiter) { + return withDelimiter(Character.toString(delimiter)); + } + + /** + *

Set the delimiter with the given {@link String}.

+ * @param delimiter the {@link String} delimiter + * @return the current {@link Builder} at the next step + * @since 1.0.0 + */ + LengthStep withDelimiter(final String delimiter); + } + + /** + *

{@link Builder}'s step to set the length.

+ * @since 1.0.0 + */ + @FunctionalInterface + public interface LengthStep { + + /** + *

Set the length.

+ * @param length the length + * @return the current {@link Builder} at the next step + * @since 1.0.0 + */ + OptionalMaxDepthStep withLength(final int length); + } + + /** + *

{@link Builder}'s optional step to set the maximum depth.

+ * @since 1.0.0 + */ + public interface OptionalMaxDepthStep extends OptionalEvaluatorStep { + + /** + *

Set the maximum depth.

+ * @param maxDepth the maximum depth + * @return the current {@link Builder} at the next step + * @since 1.0.0 + */ + OptionalEvaluatorStep withMaxDepth(final int maxDepth); + } + + /** + *

{@link Builder}'s optional step to add {@link ConstraintEvaluator}s and {@link EstimationEvaluator}s.

+ * @since 1.0.0 + */ + public interface OptionalEvaluatorStep extends BuildStep { + + /** + *

Add a {@link ConstraintEvaluator} with the given validator {@link Predicate} on every value.

+ * @param validator the validator {@link Predicate} + * @return the current {@link Builder} at the next step + * @throws NullPointerException if the validator {@link Predicate} is {code null} + * @since 1.0.0 + */ + OptionalEvaluatorStep withConstraint(final Predicate validator); + + /** + *

Add a {@link ConstraintEvaluator} with the given validator {@link Predicate} on values at provided + * indexes.

+ * @param validator the validator {@link Predicate} + * @param indexes values' indexes + * @return the current {@link Builder} at the next step + * @throws NullPointerException if the validator {@link Predicate} or the array of indexes is {code null} + * @throws IllegalArgumentException if the array of indexes is empty or if any of them is not valid + * @since 1.0.0 + */ + OptionalEvaluatorStep withConstraint(final Predicate validator, final int... indexes); + + /** + *

Add an {@link EstimationEvaluator} with the given transformer {@link Function} on every value.

+ * @param transformer the transformer {@link Function} + * @return the current {@link Builder} at the next step + * @throws NullPointerException if the transformer {@link Function} is {code null} + * @since 1.0.0 + */ + OptionalEvaluatorStep withEstimation(final Function transformer); + + /** + *

Add an {@link EstimationEvaluator} with the given transformer {@link Function} on values at provided + * indexes.

+ * @param transformer the transformer {@link Function} + * @param indexes values' indexes + * @return the current {@link Builder} at the next step + * @throws NullPointerException if the transformer {@link Function} or the array of indexes is {code null} + * @throws IllegalArgumentException if the array of indexes is empty or if any of them is not valid + * @since 1.0.0 + */ + OptionalEvaluatorStep withEstimation(final Function transformer, final int... indexes); + } + + /** + *

{@link Builder}'s build step.

+ * @since 1.0.0 + */ + @FunctionalInterface + public interface BuildStep { + + /** + *

Build the {@link DsvMender}.

+ * @return the built {@link DsvMender} + * @since 1.0.0 + */ + DsvMender build(); + } + + /** + *

Step builder to build {@link DsvMender} instances.

+ * @since 1.0.0 + */ + private static class Builder implements DelimiterStep, LengthStep, OptionalMaxDepthStep { + + /** + *

Default maximum depth.

+ * @since 1.0.0 + */ + private static final int DEFAULT_MAX_DEPTH = 20; + + /** + *

{@link DsvMender}'s delimiter.

+ * @since 1.0.0 + */ + private String delimiter; + + /** + *

{@link DsvMender}'s length.

+ * @since 1.0.0 + */ + private int length; + + /** + *

{@link DsvMender}'s maximum depth.

+ * @since 1.0.0 + */ + private int maxDepth = DEFAULT_MAX_DEPTH; + + /** + *

{@link DsvMender}'s {@link Set} of {@link ConstraintEvaluator}s.

+ * @since 1.0.0 + */ + private final Set> constraintEvaluators = new HashSet<>(); + + /** + *

{@link DsvMender}'s {@link Set} of {@link EstimationEvaluator}s.

+ * @since 1.0.0 + */ + private final Set> estimationEvaluators = new HashSet<>(); + + @Override + public LengthStep withDelimiter(final String delimiter) { + this.delimiter = delimiter; + return this; + } + + @Override + public OptionalMaxDepthStep withLength(final int length) { + this.length = length; + return this; + } + + @Override + public OptionalEvaluatorStep withMaxDepth(final int maxDepth) { + this.maxDepth = maxDepth; + return this; + } + + @Override + public OptionalEvaluatorStep withConstraint(final Predicate validator) { + return withConstraint(validator, IntStream.range(0, length).toArray()); + } + + @Override + public OptionalEvaluatorStep withConstraint(final Predicate validator, final int... indexes) { + Ensure.notNull("validator", validator); + Ensure.notNullAndNotEmpty("indexes", indexes); + for (final var index : indexes) { + Ensure.between("indexes index", index, 0, length - 1); + constraintEvaluators.add(new ConstraintEvaluator<>(values -> validator.test(values[index]))); + } + return this; + } + + @Override + public OptionalEvaluatorStep withEstimation(final Function transformer) { + return withEstimation(transformer, IntStream.range(0, length).toArray()); + } + + @Override + public OptionalEvaluatorStep withEstimation(final Function transformer, final int... indexes) { + Ensure.notNull("transformer", transformer); + Ensure.notNullAndNotEmpty("indexes", indexes); + for (final var index : indexes) { + Ensure.between("indexes index", index, 0, length - 1); + estimationEvaluators.add(new EstimationEvaluator<>(values -> transformer.apply(values[index]))); + } + return this; + } + + @Override + public DsvMender build() { + return new DsvMender(delimiter, length, maxDepth, constraintEvaluators, estimationEvaluators); + } + } + + /** + *

Delimiter.

+ * @since 1.0.0 + */ + private final String delimiter; + + /** + *

Length.

+ * @since 1.0.0 + */ + private final int length; + + /** + *

Maximum depth.

+ * @since 1.0.0 + */ + private final int maxDepth; + + /** + *

{@link Set} of {@link ConstraintEvaluator}s.

+ * @since 1.0.0 + */ + private final Set> constraintEvaluators; + + /** + *

{@link Set} of {@link EstimationEvaluator}s.

+ * @since 1.0.0 + */ + private final Set> estimationEvaluators; + + /** + *

Last {@link #mend(String...)} result or {@code null}.

+ * @since 1.0.0 + */ + private DsvMendResult lastResult; + + /** + *

Constructor with a delimiter, a length, a maximum depth, a {@link Set} of {@link ConstraintEvaluator}s and a + * {@link Set} of {@link EstimationEvaluator}s.

+ * @param delimiter the delimiter + * @param length the length + * @param maxDepth the maximum depth + * @param constraintEvaluators the {@link Set} of {@link ConstraintEvaluator}s + * @param estimationEvaluators the {@link Set} of {@link EstimationEvaluator}s + * @throws NullPointerException if the delimiter, the {@link Set} of {@link ConstraintEvaluator}s or any of them or + * the {@link Set} of {@link EstimationEvaluator}s or any of them if {@code null} + * @throws IllegalArgumentException if delimiter is empty, the length is lower than 2, the maximum depth if lower + * than 1 or if the {@link Set} of {@link ConstraintEvaluator}s or the {@link Set} of {@link EstimationEvaluator}s + * is empty + * @since 1.0.0 + */ + public DsvMender(final String delimiter, final int length, final int maxDepth, final Set> constraintEvaluators, final Set> estimationEvaluators) { + Ensure.notNullAndNotEmpty("delimiter", delimiter); + Ensure.greaterThanOrEqualTo("length", length, 2); + Ensure.greaterThanOrEqualTo("maxDepth", maxDepth, 1); + Ensure.notNullAndNotNullElements("constraintEvaluators", constraintEvaluators); + Ensure.notNullAndNotNullElements("estimationEvaluators", estimationEvaluators); + this.delimiter = delimiter; + this.length = length; + this.maxDepth = maxDepth; + this.constraintEvaluators = Set.copyOf(constraintEvaluators); + this.estimationEvaluators = Set.copyOf(estimationEvaluators); + } + + /** + *

Test if values are valid based on the length and {@link ConstraintEvaluator}s.

+ * @param values values to test + * @return {@code true} if values are valid + * @since 1.0.0 + */ + private boolean isValid(final String[] values) { + if (length != values.length) { + return false; + } + for (final var constraintEvaluator : constraintEvaluators) { + if (!constraintEvaluator.isValid(values)) { + return false; + } + } + return true; + } + + /** + *

Could optimize the given DSV row by merging consecutive empty values to improve the {@link #mend(String...)} + * operation.

+ * @param threshold the threshold of consecutive empty values to keep + * @param row the row to optimize + * @return optimized values + * @throws NullPointerException if the row is {@code null} + * @throws IllegalArgumentException if the threshold is lower than {@code 0} + * @since 1.0.0 + */ + public String[] optimize(final int threshold, final String row) { + Ensure.notNull("row", row); + return optimize(threshold, Strings.split(delimiter, row).toArray(String[]::new)); + } + + /** + *

Could optimize given values by merging consecutive empty values to improve the {@link #mend(String...)} + * operation.

+ * @param threshold the threshold of consecutive empty values to keep + * @param values values to optimize + * @return optimized values + * @throws NullPointerException if values or any of them is {@code null} + * @throws IllegalArgumentException if the threshold is lower than {@code 0} + * @since 1.0.0 + */ + public String[] optimize(final int threshold, final String... values) { + Ensure.greaterThanOrEqualTo("threshold", threshold, 0); + Ensure.notNullAndNotNullElements("values", values); + var optimizedValues = values.clone(); + while (length < optimizedValues.length) { + var from = 0; + var to = 0; + var index = -1; + for (var i = 0; i < optimizedValues.length; ++i) { + if (-1 == index && optimizedValues[i].isEmpty()) { + index = i; + continue; + } + if (-1 != index && !optimizedValues[i].isEmpty()) { + if (to - from < i - index) { + from = index; + to = i; + } + index = -1; + } + } + if (-1 != index && to - from < optimizedValues.length - index) { + from = index; + to = optimizedValues.length; + } + if (to - from > 2 * threshold + 1) { + for (var i = from + threshold + 1; i < to - threshold; ++i) { + optimizedValues = ObjectArrays.remove(optimizedValues, from + threshold + 1); + optimizedValues[from + threshold] += delimiter; + } + } else { + break; + } + } + return optimizedValues; + } + + /** + *

Mend the given DSV row if needed.

+ * @param row the row to mend + * @return best fixed values + * @throws MendException might occurs if mending the value is not possible + * @throws NullPointerException if the row is {@code null} + * @since 1.0.0 + */ + public String[] mend(final String row) { + Ensure.notNull("row", row); + return mend(Strings.split(delimiter, row).toArray(String[]::new)); + } + + /** + *

Mend given values if needed.

+ * @param values values to mend + * @return best fixed values + * @throws MendException might occurs if mending the value is not possible + * @throws NullPointerException if values or any of them is {@code null} + * @since 1.0.0 + */ + @Override + public String[] mend(final String... values) { + Ensure.notNullAndNotNullElements("values", values); + lastResult = null; + if (isValid(values)) { + for (final var estimationEvaluator : estimationEvaluators) { + estimationEvaluator.fit(values); + } + return values; + } + final var depth = Math.abs(length - values.length - 2); + Ensure.lowerThanOrEqualTo("values depth", depth, maxDepth); + final var children = new ArrayList(); + if (length < values.length) { + children.addAll(generateJoinChildren(values)); + for (var i = length; i < values.length - 1; ++i) { + final var tmpChildren = children.toArray(String[][]::new); + children.clear(); + for (final var tmpChild : tmpChildren) { + children.addAll(generateJoinChildren(tmpChild)); + } + } + } else if (length > values.length) { + children.addAll(generateShiftChildren(values)); + for (var i = length; i > values.length + 1; --i) { + final var tmpChildren = children.toArray(String[][]::new); + children.clear(); + for (final var tmpChild : tmpChildren) { + children.addAll(generateShiftChildren(tmpChild)); + } + } + } else { + final var tmpChildren = generateJoinChildren(values); + for (final var tmpChild : tmpChildren) { + children.addAll(generateShiftChildren(tmpChild)); + } + } + final var candidates = new HashSet(); + DsvMendCandidate bestCandidate = null; + for (final var child : children) { + final var optionalCandidateScore = DoubleStream.concat( + constraintEvaluators.stream().mapToDouble(constraintEvaluator -> constraintEvaluator.evaluate(child)), + estimationEvaluators.stream().mapToDouble(estimationEvaluator -> estimationEvaluator.evaluate(child)) + ).average(); + if (optionalCandidateScore.isPresent()) { + final var candidateScore = optionalCandidateScore.getAsDouble(); + final var candidate = new DsvMendCandidate(child, candidateScore); + candidates.add(candidate); + if (!Double.isNaN(candidateScore) && (null == bestCandidate || bestCandidate.getScore() < candidateScore)) { + bestCandidate = candidate; + } + } + } + if (null == bestCandidate) { + throw new MendException("No solution for values: " + ToString.toString(values) + " (consider using others constraints and estimations)"); + } + lastResult = new DsvMendResult(values, candidates, bestCandidate); + return bestCandidate.getValue(); + } + + /** + *

Generate a {@link List} of every possibility of joining consecutive values with the delimiter.

+ * @param parent parent values + * @return a {@link List} of joining possibilities + * @since 1.0.0 + */ + private List generateJoinChildren(final String[] parent) { + final var children = new ArrayList(parent.length - 1); + for (var i = 0; i < parent.length - 1; ++i) { + final var child = new String[parent.length - 1]; + for (var j = 0; j < parent.length - 1; ++j) { + if (j == i) { + child[j] = parent[j] + delimiter + parent[j + 1]; + } else { + child[j] = parent[j < i ? j : j + 1]; + } + } + children.add(child); + } + return children; + } + + /** + *

Generate a {@link List} of every possibility of shifting consecutive values with an empty one.

+ * @param parent parent values + * @return a {@link List} of shifting possibilities + * @since 1.0.0 + */ + private List generateShiftChildren(final String[] parent) { + final var children = new ArrayList(parent.length + 1); + for (var i = 0; i < parent.length + 1; ++i) { + final var child = new String[parent.length + 1]; + for (var j = 0; j < parent.length + 1; ++j) { + if (j == i) { + child[j] = Strings.EMPTY; + } else { + child[j] = parent[j < i ? j : j - 1]; + } + } + children.add(child); + } + return children; + } + + @Override + public Optional getLastResult() { + return Optional.ofNullable(lastResult); + } + + /** + *

Get the delimiter.

+ * @return the delimiter + * @since 1.0.0 + */ + public String getDelimiter() { + return delimiter; + } + + /** + *

Get the length.

+ * @return the length + * @since 1.0.0 + */ + public int getLength() { + return length; + } + + /** + *

Get the maximum depth.

+ * @return the maximum depth + * @since 1.0.0 + */ + public int getMaxDepth() { + return maxDepth; + } + + /** + *

Get the {@link Set} of {@link ConstraintEvaluator}s.

+ * @return the {@link Set} of {@link ConstraintEvaluator}s + * @since 1.0.0 + */ + public Set> getConstraintEvaluators() { + return constraintEvaluators; + } + + /** + *

Get the {@link Set} of {@link EstimationEvaluator}s.

+ * @return the {@link Set} of {@link EstimationEvaluator}s + * @since 1.0.0 + */ + public Set> getEstimationEvaluators() { + return estimationEvaluators; + } + + /** + *

Create a new {@link Builder} instance.

+ * @return the created {@link Builder} + * @since 1.0.0 + */ + public static DelimiterStep builder() { + return new Builder(); + } + + /** + *

Create a new basic {@link DsvMender} instance without any {@link ConstraintEvaluator} and with some + * {@link EstimationEvaluator}s.

+ * @param delimiter the {@code char} delimiter + * @param length the length + * @return the created basic {@link DsvMender} + * @since 1.0.0 + */ + public static DsvMender basic(final char delimiter, final int length) { + return basic(Character.toString(delimiter), length); + } + + /** + *

Create a new basic {@link DsvMender} instance without any {@link ConstraintEvaluator} and with some + * {@link EstimationEvaluator}s.

+ * @param delimiter the {@link String} delimiter + * @param length the length + * @return the created basic {@link DsvMender} + * @since 1.0.0 + */ + public static DsvMender basic(final String delimiter, final int length) { + return builder() + .withDelimiter(delimiter) + .withLength(length) + .withEstimation(String::isEmpty) + .withEstimation(String::length) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/github/alexisjehan/mender/dsv/package-info.java b/src/main/java/com/github/alexisjehan/mender/dsv/package-info.java new file mode 100644 index 0000000..9916be0 --- /dev/null +++ b/src/main/java/com/github/alexisjehan/mender/dsv/package-info.java @@ -0,0 +1,29 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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. + */ +/** + *

{@link com.github.alexisjehan.mender.dsv.DsvMender} implementation of + * {@link com.github.alexisjehan.mender.api.Mender} and its components.

+ * @since 1.0.0 + */ +package com.github.alexisjehan.mender.dsv; \ No newline at end of file diff --git a/src/main/java/examples/BombFixExample.java b/src/main/java/examples/BombFixExample.java deleted file mode 100644 index 609a0bd..0000000 --- a/src/main/java/examples/BombFixExample.java +++ /dev/null @@ -1,57 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 examples; - -import java.util.regex.Pattern; - -import org.mender.MenderException; -import org.mender.dsv.DsvMender; - -public final class BombFixExample { - - public static void main(final String... args) { - final DsvMender mender = DsvMender.builder(",", 4, 30) // Node depth of 30 to increase default value - .withNonEmptyConstraints() - .withPatternConstraint(0, Pattern.compile("\\d+")) - .withStartsWithConstraint(2, "\"") - .withEndsWithConstraint(2, "\"") - .build(); - try { - // Takes so many memory and may fail because of nodes bomb - //printValues(mender.fix("123,Thug-man,\"Deal with it,,,,,,,,,,,,,,,,,,,,,,,,\",2001-02-03")); - - // Optimized for repeated delimiter chars - printValues(mender.optimizedFix("123,Thug-man,\"Deal with it,,,,,,,,,,,,,,,,,,,,,,,,\",2001-02-03", 1)); - } catch (final MenderException e) { - e.printStackTrace(); - } - } - - private static void printValues(final String[] values) { - for (final String value : values) { - System.out.print("\"" + value.replace("\"", "\\\"") + "\"\t"); - } - System.out.println(); - } -} \ No newline at end of file diff --git a/src/main/java/examples/ConcreteExample.java b/src/main/java/examples/ConcreteExample.java new file mode 100644 index 0000000..aa972b4 --- /dev/null +++ b/src/main/java/examples/ConcreteExample.java @@ -0,0 +1,82 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 examples; + +import com.github.alexisjehan.javanilla.lang.Strings; +import com.github.alexisjehan.mender.dsv.DsvMender; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.Arrays; +import java.util.stream.Collectors; + +public final class ConcreteExample { + + private static final String DELIMITER = ","; + private static final int LENGTH = 3; + + // Source: https://en.wikipedia.org/wiki/Java_version_history + private static final String DATA = String.join( + System.lineSeparator(), + String.join(DELIMITER, "Release", "Release date", "Highlights"), + String.join(DELIMITER, "Java SE 9", "2017-09-21", "Initial release"), + String.join(DELIMITER, "Java SE 9.0.1", "2017-10-17", "October 2017 security fixes and critical bug fixes"), + String.join(DELIMITER, "Java SE 9.0.4", "2018-01-16", "Final release for JDK 9; January 2018 security fixes and critical bug fixes"), + String.join(DELIMITER, "Java SE 10", "2018-03-20", "Initial release"), + String.join(DELIMITER, "Java SE 10.0.1", "2018-04-17", "Security fixes, 5 bug fixes"), // One value contains the delimiter + String.join(DELIMITER, "Java SE 11", "2018-09-25", "Initial release"), + String.join(DELIMITER, "Java SE 11.0.1", "2018-10-16", "Security & bug fixes"), + String.join(DELIMITER, "Java SE 11.0.2", "2019-01-15", "Security & bug fixes"), + String.join(DELIMITER, "Java SE 12", "Initial release") // Missing the release date value + ); + + private ConcreteExample() { + // Not available + } + + public static void main(final String... args) throws IOException { + final var mender = DsvMender.builder() + .withDelimiter(DELIMITER) + .withLength(LENGTH) + .withConstraint(value -> value.startsWith("Java SE"), 0) // values[0] must start with "Java SE" + .withConstraint(value -> value.isEmpty() || 10 == value.length(), 1)// values[1] must be empty or have a length of 10 + .build(); + try (final var reader = new BufferedReader(new StringReader(DATA))) { + printValues(Strings.split(mender.getDelimiter(), reader.readLine()).toArray(String[]::new)); // Header + String row; + while (null != (row = reader.readLine())) { + printValues(mender.mend(row)); + } + } + } + + private static void printValues(final String[] values) { + System.out.println( + Arrays.stream(values) + .map(Strings::quote) + .collect(Collectors.joining(", ")) + ); + } +} \ No newline at end of file diff --git a/src/main/java/examples/DetailsExample.java b/src/main/java/examples/DetailsExample.java deleted file mode 100644 index a34883d..0000000 --- a/src/main/java/examples/DetailsExample.java +++ /dev/null @@ -1,60 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 examples; - -import java.util.Arrays; -import java.util.Map; - -import org.mender.MenderException; -import org.mender.dsv.DsvMender; - -public class DetailsExample { - - public static void main(final String... args) { - final DsvMender mender = DsvMender.builder("\t", 3) - .withContainsEstimations("1") - .withContainsEstimations("2") - .withContainsEstimations("3") - .build(); - mender.fit("11\t22\t33"); - mender.fit("1111\t2222\t3333"); - try { - printFixDetails(mender, "1111\t1111\t22222222\t33333333"); - } catch (final MenderException e) { - e.printStackTrace(); - } - } - - private static void printFixDetails(final DsvMender mender, final String toFix) throws MenderException { - final String[] best = mender.fix(toFix); - System.out.println("Computed nodes:"); - final Map nodes = mender.getNodeScores(); - for (final Map.Entry node : nodes.entrySet()) { - System.out.println(Arrays.toString(node.getKey()) + " (score=" + node.getValue() + ")"); - } - System.out.println(); - System.out.println("Best node: " + Arrays.toString(best) + " (score=" + mender.getScore() + ")"); - System.out.println(); - } -} \ No newline at end of file diff --git a/src/main/java/examples/MissingCellsExample.java b/src/main/java/examples/MissingCellsExample.java deleted file mode 100644 index 3a63bb9..0000000 --- a/src/main/java/examples/MissingCellsExample.java +++ /dev/null @@ -1,62 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 examples; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.regex.Pattern; - -import org.mender.MenderException; -import org.mender.dsv.DsvMender; -import org.mender.dsv.DsvReader; - -public final class MissingCellsExample { - - public static void main(final String... args) { - final DsvMender mender = DsvMender.builder("\t", 5) - .withPatternConstraint(0, Pattern.compile("[0-9]+")) // The year column is always numerical - .withMaxLengthConstraint(1, 6) // The make column can't be more than 10 characters - .build(); - try (final DsvReader reader = new DsvReader(mender, new InputStreamReader(MissingCellsExample.class.getClassLoader().getResourceAsStream("missing_cells.tsv")))) { - try { - printValues(reader.readHeader()); - String[] row; - while (null != (row = reader.readRow())) { - printValues(row); - } - } catch (final MenderException e) { - e.printStackTrace(); - } - } catch (final IOException e) { - e.printStackTrace(); - } - } - - private static void printValues(final String[] values) { - for (final String value : values) { - System.out.print("\"" + value.replace("\"", "\\\"") + "\"\t"); - } - System.out.println(); - } -} \ No newline at end of file diff --git a/src/main/java/examples/NotQuotedExample.java b/src/main/java/examples/NotQuotedExample.java deleted file mode 100644 index 4ca7834..0000000 --- a/src/main/java/examples/NotQuotedExample.java +++ /dev/null @@ -1,64 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 examples; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.util.regex.Pattern; - -import org.mender.MenderException; -import org.mender.dsv.DsvMender; -import org.mender.dsv.DsvReader; - -public final class NotQuotedExample { - - public static void main(final String... args) { - final DsvMender mender = DsvMender.builder(",", 5) - .withLengthEstimations() // Estimating the length of the value for every columns - .withContainsEstimations(" ") // Estimating if the value contains a space character for every columns - .withPatternConstraint(0, Pattern.compile("[0-9]+")) // The ID column is always numerical, not empty - .withLengthConstraint(3, 10) // The birthday column always contains 10 characters - .build(); - try (final DsvReader reader = new DsvReader(mender, new InputStreamReader(NotQuotedExample.class.getClassLoader().getResourceAsStream("not_quoted.csv")))) { - try { - printValues(reader.readHeader()); - String[] row; - while (null != (row = reader.readRow())) { - printValues(row); - } - } catch (final MenderException e) { - e.printStackTrace(); - } - } catch (final IOException e) { - e.printStackTrace(); - } - } - - private static void printValues(final String[] values) { - for (final String value : values) { - System.out.print("\"" + value.replace("\"", "\\\"") + "\"\t"); - } - System.out.println(); - } -} \ No newline at end of file diff --git a/src/main/java/examples/OptimizeExample.java b/src/main/java/examples/OptimizeExample.java new file mode 100644 index 0000000..29a94a3 --- /dev/null +++ b/src/main/java/examples/OptimizeExample.java @@ -0,0 +1,67 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 examples; + +import com.github.alexisjehan.mender.dsv.DsvMender; +import com.github.alexisjehan.javanilla.lang.Strings; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public final class OptimizeExample { + + private static final char DELIMITER = ','; + private static final int LENGTH = 3; + + private OptimizeExample() { + // Not available + } + + public static void main(final String... args) { + final var mender = DsvMender.builder() + .withDelimiter(DELIMITER) + .withLength(LENGTH) + .withMaxDepth(Integer.MAX_VALUE) + .withConstraint("foo"::equals, 0) // values[0] must be "foo" + .withConstraint("bar"::equals, 2) // values[2] must be "bar" + .build(); + + final var row = "foo,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,bar"; + + // Not optimized, out of memory + //printValues(mender.mend(row)); + + // Optimized, can be computed + final var threshold = 1; + printValues(mender.mend(mender.optimize(threshold, row))); + } + + private static void printValues(final String[] values) { + System.out.println( + Arrays.stream(values) + .map(Strings::quote) + .collect(Collectors.joining(", ")) + ); + } +} \ No newline at end of file diff --git a/src/main/java/examples/SimpleExample.java b/src/main/java/examples/SimpleExample.java new file mode 100644 index 0000000..9fa6a49 --- /dev/null +++ b/src/main/java/examples/SimpleExample.java @@ -0,0 +1,86 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 examples; + +import com.github.alexisjehan.mender.dsv.DsvMender; +import com.github.alexisjehan.javanilla.lang.Strings; + +import java.util.Arrays; +import java.util.stream.Collectors; + +public final class SimpleExample { + + private static final String DELIMITER = "\t"; + private static final int LENGTH = 3; + + private SimpleExample() { + // Not available + } + + public static void main(final String... args) { + final var mender = DsvMender.builder() + .withDelimiter(DELIMITER) + .withLength(LENGTH) + .withEstimation(value -> value.contains("1")) // Estimation on all values containing "1" + .withEstimation(value -> value.contains("2")) // Estimation on all values containing "2" + .withEstimation(value -> value.contains("3")) // Estimation on all values containing "3" + .build(); + + // Valid + mender.mend("11", "22", "33"); + mender.mend("111", "222", "333"); + + // Invalid + mender.mend( "11", "2", "2", "33"); + + // ["11", "2", "2\t33"] -> 0.9 (Because the frequency of values[2] containing "2" is 0.0) + // ["11\t2", "2", "33"] -> 0.9 (Because the frequency of values[0] containing "2" is 0.0) + // ["11", "2\t2", "33"] -> 1.0 (Because the frequency of values[1] containing "2" is 1.0) + printResult(mender); + } + + private static void printResult(final DsvMender mender) { + final var result = mender.getLastResult().orElseThrow(); + System.out.println("Candidates:"); + for (final var candidate : result.getCandidates()) { + System.out.println( + Arrays.stream(candidate.getValue()) + .map(Strings::quote) + .collect(Collectors.joining(", ")) + + " -> " + + candidate.getScore() + ); + } + System.out.println(); + System.out.println("Best candidate:"); + final var bestCandidate = result.getBestCandidate(); + System.out.println( + Arrays.stream(bestCandidate.getValue()) + .map(Strings::quote) + .collect(Collectors.joining(", ")) + + " -> " + + bestCandidate.getScore() + ); + } +} \ No newline at end of file diff --git a/src/main/java/org/mender/Evaluator.java b/src/main/java/org/mender/Evaluator.java deleted file mode 100644 index 227435f..0000000 --- a/src/main/java/org/mender/Evaluator.java +++ /dev/null @@ -1,60 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender; - -/** - *

An {@code Evaluator} is some kind of internal {@link Mender} component that is able to evaluate an element based - * on previous ones that were given to be adjusted. The evaluation can be done using - * {@link org.mender.criteria.Constraint} or {@link org.mender.criteria.Estimation} objects.

- * - * @param Element's type - * @since 1.0 - */ -public interface Evaluator { - - /** - *

Check if the element passes all configured {@code Constraint} objects.

- * - * @param element The element to check - * @return {@code true} if the element passes all configured {@code Constraint} objects - */ - boolean checkConstraints(final E element); - - /** - *

Adjust the {@code Evaluator} using the given element (expected to adjust configured {@code Estimation} - * objects).

- * - * @param element The element to adjust - */ - void adjustEstimations(final E element); - - /** - *

Evaluate the given element by returning a score (the score should be computed with {@code Constraint} or - * {@code Estimation} objects).

- * - * @param element The element to evaluate - * @return An evaluation score, preferably between 0 and 1 - */ - double evaluate(final E element); -} \ No newline at end of file diff --git a/src/main/java/org/mender/Mender.java b/src/main/java/org/mender/Mender.java deleted file mode 100644 index d320fae..0000000 --- a/src/main/java/org/mender/Mender.java +++ /dev/null @@ -1,49 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender; - -/** - *

A {@code Mender} aims to fix invalids elements by learning from valid ones.

- * - * @param Element's type - * @since 1.0 - */ -public interface Mender { - - /** - *

Learn from a valid element, for example that can be done using an {@link Evaluator}.

- * - * @param element The valid element to learn from - */ - void fit(final E element); - - /** - *

Fix an invalid element using custom strategies and knowledges.

- * - * @param element The invalid element to repair - * @return Fixed version of the element - * @throws MenderException In particular conditions the {@code Mender} might not be able to fix the given element - */ - E fix(final E element) throws MenderException; -} \ No newline at end of file diff --git a/src/main/java/org/mender/MenderException.java b/src/main/java/org/mender/MenderException.java deleted file mode 100644 index 82a854f..0000000 --- a/src/main/java/org/mender/MenderException.java +++ /dev/null @@ -1,46 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender; - -/** - *

An {@code Exception} that can be thrown by a {@link Mender} in specifics cases.

- * - * @since 1.0 - */ -public class MenderException extends Exception { - - /** - *

Generated serialization UID

- */ - private static final long serialVersionUID = -6841482368498568972L; - - /** - *

Constructor of a {@code MenderException} using a message.

- * - * @param message The message of the {@code Exception} - */ - public MenderException(final String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/criteria/Constraint.java b/src/main/java/org/mender/criteria/Constraint.java deleted file mode 100644 index 5b91a1f..0000000 --- a/src/main/java/org/mender/criteria/Constraint.java +++ /dev/null @@ -1,79 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.criteria; - -import java.util.function.Predicate; - -/** - *

A {@link Criterion} that returns a binary (0 or 1) score using a predicate on values.

- * - * @param value's type - * @since 1.0 - */ -public class Constraint implements Criterion { - - /** - *

Internal used predicate.

- */ - private final Predicate predicate; - - /** - *

Constructor using a predicate that will be used to test the value.

- * - * @param predicate The predicate function - * @throws NullPointerException If the predicate is {@code null} - */ - public Constraint(final Predicate predicate) { - if (null == predicate) { - throw new NullPointerException("Invalid predicate (not null expected)"); - } - this.predicate = predicate; - } - - /** - *

Check if the value passes the predicate.

- * - * @param value The value to check - * @return {@code true} if the value passes the predicate - */ - public boolean check(final V value) { - return predicate.test(value); - } - - @Override - public double calculate(final V value) { - return predicate.test(value) ? 1.0d : 0.0d; - } - - /** - *

Vanilla constructor for easiest uses with generic.

- * - * @param value's type - * @param predicate The predicate function - * @return The instantiated {@code Constraint} - */ - public static Constraint of(final Predicate predicate) { - return new Constraint<>(predicate); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/criteria/Criterion.java b/src/main/java/org/mender/criteria/Criterion.java deleted file mode 100644 index 1742167..0000000 --- a/src/main/java/org/mender/criteria/Criterion.java +++ /dev/null @@ -1,42 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.criteria; - -/** - *

A {@code Criterion} can be used to calculate a score from a value.

- * - * @param value's type - * @since 1.0 - */ -@FunctionalInterface -public interface Criterion { - - /** - *

Calculate value's score.

- * - * @param value The value to get the score from - * @return The calculated score - */ - double calculate(final V value); -} \ No newline at end of file diff --git a/src/main/java/org/mender/criteria/Estimation.java b/src/main/java/org/mender/criteria/Estimation.java deleted file mode 100644 index 35742bb..0000000 --- a/src/main/java/org/mender/criteria/Estimation.java +++ /dev/null @@ -1,107 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.criteria; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Function; - -/** - *

A {@link Criterion} that collects values, the score is equal to the frequency among all collected values.

- * - *

Note: Values are transformed using a function to be stored and collected - * (example: collect by features).

- * - * @param value's type - * @param value's store type - * @since 1.0 - */ -public class Estimation implements Criterion { - - /** - *

Internal used function.

- */ - private final Function function; - - /** - *

Bag of stored values.

- */ - private final Map bag = new HashMap<>(); - - /** - *

Total values in the bag (different of the total of unique values).

- */ - private long total = 0; - - /** - *

Constructor using a function that will be used to be applied to values.

- * - * @param function The function - * @throws NullPointerException If the function is {@code null} - */ - public Estimation(final Function function) { - if (null == function) { - throw new NullPointerException("Invalid function (not null expected)"); - } - this.function = function; - } - - /** - *

Adjust by adding a value.

- * - * @param value The value to add - */ - public void adjust(final V value) { - final S key = function.apply(value); - if (bag.containsKey(key)) { - bag.get(key).incrementAndGet(); - } else { - bag.put(key, new AtomicLong(1L)); - } - ++total; - } - - @Override - public double calculate(final V value) { - final S key = function.apply(value); - if (0 < total && bag.containsKey(key)) { - return (double) bag.get(key).get() / total; - } else { - return 0.0d; - } - } - - /** - *

Vanilla constructor for easiest uses with generic.

- * - * @param value's type - * @param value's store type - * @param function The function - * @return The instantiated {@code Estimation} - */ - public static Estimation of(final Function function) { - return new Estimation<>(function); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/criteria/package-info.java b/src/main/java/org/mender/criteria/package-info.java deleted file mode 100644 index 10ac908..0000000 --- a/src/main/java/org/mender/criteria/package-info.java +++ /dev/null @@ -1,29 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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. -*/ -/** - *

Criteria components that can be used by an {@link org.mender.Evaluator} to compute a score.

- * - * @since 1.0 - */ -package org.mender.criteria; \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/DsvBuilder.java b/src/main/java/org/mender/dsv/DsvBuilder.java deleted file mode 100644 index 43d852c..0000000 --- a/src/main/java/org/mender/dsv/DsvBuilder.java +++ /dev/null @@ -1,602 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; -import java.util.stream.IntStream; - -import org.mender.criteria.Constraint; -import org.mender.criteria.Estimation; - -/** - *

Builder pattern implementation to easily create new {@link DsvMender} instances.

- * - * @since 1.0 - */ -public final class DsvBuilder { - - /** - *

{@code DsvEvaluator} object.

- */ - private final DsvEvaluator evaluator = new DsvEvaluator(); - - /** - *

String delimiter of the DSV data.

- */ - private final String delimiter; - - /** - *

Number of columns of the DSV data.

- */ - private final int nbColumns; - - /** - *

Maximum depth of the nodes tree.

- */ - private final int maxDepth; - - /** - *

Construct a new {@code DsvBuilder}.

- * - * @param delimiter {@code String} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @param maxDepth Maximum depth of the nodes tree - */ - DsvBuilder(final String delimiter, final int nbColumns, final int maxDepth) { - this.delimiter = delimiter; - this.nbColumns = nbColumns; - this.maxDepth = maxDepth; - } - - // EMPTY - /** - *

Add {@code String.isEmpty()} estimations to all columns of the DSV data.

- * - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withEmptyEstimations() { - return withEstimations(String::isEmpty); - } - - /** - *

Add a {@code String.isEmpty()} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withEmptyEstimation(final int index) { - return withEstimation(index, String::isEmpty); - } - - /** - *

Add a {@code String.isEmpty()} constraint to the column at specified index of the DSV data. In others words - * that means this column should always be empty.

- * - * @param index Index of the column - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withEmptyConstraint(final int index) { - return withConstraint(index, String::isEmpty); - } - - // NON EMPTY - /** - *

Add {@code String} non-empty constraints to all columns of the DSV data. In others words that means all - * columns should never be empty.

- * - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withNonEmptyConstraints() { - return withConstraints(value -> !value.isEmpty()); - } - - /** - *

Add a {@code String} non-empty constraint to the column at specified index of the DSV data. In others words - * that means this column should never be empty.

- * - * @param index Index of the column - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withNonEmptyConstraint(final int index) { - return withConstraint(index, value -> !value.isEmpty()); - } - - // LENGTH - /** - *

Add {@code String.length()} estimations to all columns of the DSV data.

- * - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withLengthEstimations() { - return withEstimations(String::length); - } - - /** - *

Add a {@code String.length()} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withLengthEstimation(final int index) { - return withEstimation(index, String::length); - } - - /** - *

Add a {@code String.length()} constraint to the column at specified index of the DSV data. In others words - * that means each value of this column should always have that length.

- * - * @param index Index of the column - * @param length The required length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the length is less than 0 - */ - public DsvBuilder withLengthConstraint(final int index, final int length) { - if (0 > length) { - throw new IllegalArgumentException("Invalid length: " + length + " (greater than or equal to 0 expected)"); - } - return withConstraint(index, value -> length == value.length()); - } - - // MIN LENGTH - /** - *

Add {@code String} minimum length estimations to all columns of the DSV data.

- * - * @param minLength The estimated minimum length for all column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 - */ - public DsvBuilder withMinLengthEstimations(final int minLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid minimum length: " + minLength + " (greater than 0 expected)"); - } - return withEstimations(value -> minLength <= value.length()); - } - - /** - *

Add a {@code String} minimum length estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param minLength The estimated minimum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 - */ - public DsvBuilder withMinLengthEstimation(final int index, final int minLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid minimum length: " + minLength + " (greater than 0 expected)"); - } - return withEstimation(index, value -> minLength <= value.length()); - } - - /** - *

Add a {@code String} minimum length constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should always have that minimum length.

- * - * @param index Index of the column - * @param minLength The required minimum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 - */ - public DsvBuilder withMinLengthConstraint(final int index, final int minLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid minimum length: " + minLength + " (greater than 0 expected)"); - } - return withConstraint(index, value -> minLength <= value.length()); - } - - // MAX LENGTH - /** - *

Add {@code String} maximum length estimations to all columns of the DSV data.

- * - * @param maxLength The estimated maximum length for all column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the maximum length is less than or equal to 0 - */ - public DsvBuilder withMaxLengthEstimations(final int maxLength) { - if (1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than 0 expected)"); - } - return withEstimations(value -> maxLength >= value.length()); - } - - /** - *

Add a {@code String} maximum length estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param maxLength The estimated maximum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the maximum length is less than or equal to 0 - */ - public DsvBuilder withMaxLengthEstimation(final int index, final int maxLength) { - if (1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than 0 expected)"); - } - return withEstimation(index, value -> maxLength >= value.length()); - } - - /** - *

Add a {@code String} maximum length constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should always have that maximum length.

- * - * @param index Index of the column - * @param maxLength The required maximum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the maximum length is less than or equal to 0 - */ - public DsvBuilder withMaxLengthConstraint(final int index, final int maxLength) { - if (1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than 0 expected)"); - } - return withConstraint(index, value -> maxLength >= value.length()); - } - - // RANGE LENGTH - /** - *

Add {@code String} range length estimations to all columns of the DSV data.

- * - * @param minLength The estimated minimum length for all column values - * @param maxLength The estimated maximum length for all column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 or if the maximum length is - * greater than the minimum length - */ - public DsvBuilder withRangeLengthEstimations(final int minLength, final int maxLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid maximum length: " + minLength + " (greater than 0 expected)"); - } - if (minLength + 1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than minimum length expected)"); - } - return withEstimations(value -> minLength <= value.length() && maxLength >= value.length()); - } - - /** - *

Add a {@code String} range length estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param minLength The estimated minimum length for specified column values - * @param maxLength The estimated maximum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 or if the maximum length is - * greater than the minimum length - */ - public DsvBuilder withRangeLengthEstimation(final int index, final int minLength, final int maxLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid maximum length: " + minLength + " (greater than 0 expected)"); - } - if (minLength + 1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than minimum length expected)"); - } - return withEstimation(index, value -> minLength <= value.length() && maxLength >= value.length()); - } - - /** - *

Add a {@code String} range length constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should always have that minimum and maximum lengths.

- * - * @param index Index of the column - * @param minLength The required minimum length for specified column values - * @param maxLength The required maximum length for specified column values - * @return The {@code DsvBuilder} instance - * @throws IllegalArgumentException If the minimum length is less than or equal to 0 or if the maximum length is - * greater than the minimum length - */ - public DsvBuilder withRangeLengthConstraint(final int index, final int minLength, final int maxLength) { - if (1 > minLength) { - throw new IllegalArgumentException("Invalid maximum length: " + minLength + " (greater than 0 expected)"); - } - if (minLength + 1 > maxLength) { - throw new IllegalArgumentException("Invalid maximum length: " + maxLength + " (greater than minimum length expected)"); - } - return withConstraint(index, value -> minLength <= value.length() && maxLength >= value.length()); - } - - // PATTERN - /** - *

Add {@link Pattern} estimations to all columns of the DSV data.

- * - * @param pattern The estimated matched {@code Pattern} by all column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the pattern is null - */ - public DsvBuilder withPatternEstimations(final Pattern pattern) { - if (null == pattern) { - throw new NullPointerException("Invalid pattern (not null expected)"); - } - return withEstimations(value -> pattern.matcher(value).matches()); - } - - /** - *

Add a {@link Pattern} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param pattern The estimated matched {@code Pattern} by specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the pattern is {@code null} - */ - public DsvBuilder withPatternEstimation(final int index, final Pattern pattern) { - if (null == pattern) { - throw new NullPointerException("Invalid pattern (not null expected)"); - } - return withEstimation(index, value -> pattern.matcher(value).matches()); - } - - /** - *

Add a {@link Pattern} constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should always match that {@code Pattern}.

- * - * @param index Index of the column - * @param pattern The required matched {@code Pattern} by specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the pattern is {@code null} - */ - public DsvBuilder withPatternConstraint(final int index, final Pattern pattern) { - if (null == pattern) { - throw new NullPointerException("Invalid pattern (not null expected)"); - } - return withConstraint(index, value -> pattern.matcher(value).matches()); - } - - // CONTAINS - /** - *

Add {@code String.contains()} estimations to all columns of the DSV data.

- * - * @param substring The estimated contained substring by all column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the substring is {@code null} - */ - public DsvBuilder withContainsEstimations(final String substring) { - if (null == substring) { - throw new NullPointerException("Invalid substring (not null expected)"); - } - return withEstimations(value -> value.contains(substring)); - } - - /** - *

Add a {@code String.contains()} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param substring The estimated contained substring by specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the substring is {@code null} - */ - public DsvBuilder withContainsEstimation(final int index, final String substring) { - if (null == substring) { - throw new NullPointerException("Invalid substring (not null expected)"); - } - return withEstimation(index, value -> value.contains(substring)); - } - - /** - *

Add a {@code String.contains()} constraint to the column at specified index of the DSV data. In others words - * that means each value of this column should always contain the substring.

- * - * @param index Index of the column - * @param substring The required contained substring by specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the substring is {@code null} - */ - public DsvBuilder withContainsConstraint(final int index, final String substring) { - if (null == substring) { - throw new NullPointerException("Invalid substring (not null expected)"); - } - return withConstraint(index, value -> value.contains(substring)); - } - - // CONTAINS NONE - /** - *

Add a {@code String} contains-none constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should never contain the substring.

- * - * @param index Index of the column - * @param substring The required not contained substring by specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the substring is {@code null} - */ - public DsvBuilder withContainsNoneConstraint(final int index, final String substring) { - if (null == substring) { - throw new NullPointerException("Invalid substring (not null expected)"); - } - return withConstraint(index, value -> !value.contains(substring)); - } - - // STARTS WITH - /** - *

Add {@code String.startsWith()} estimations to all columns of the DSV data.

- * - * @param prefix The estimated prefix of all column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the prefix is {@code null} - */ - public DsvBuilder withStartsWithEstimations(final String prefix) { - if (null == prefix) { - throw new NullPointerException("Invalid prefix (not null expected)"); - } - return withEstimations(value -> value.startsWith(prefix)); - } - - /** - *

Add a {@code String.startsWith()} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param prefix The estimated prefix of specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the prefix is {@code null} - */ - public DsvBuilder withStartsWithEstimation(final int index, final String prefix) { - if (null == prefix) { - throw new NullPointerException("Invalid prefix (not null expected)"); - } - return withEstimation(index, value -> value.startsWith(prefix)); - } - - /** - *

Add a {@code String.startsWith()} constraint to the column at specified index of the DSV data. In others - * words that means each value of this column should always start with the prefix.

- * - * @param index Index of the column - * @param prefix The required prefix of specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the prefix is {@code null} - */ - public DsvBuilder withStartsWithConstraint(final int index, final String prefix) { - if (null == prefix) { - throw new NullPointerException("Invalid prefix (not null expected)"); - } - return withConstraint(index, value -> value.startsWith(prefix)); - } - - // ENDS WITH - /** - *

Add {@code String.endsWith()} estimations to all columns of the DSV data.

- * - * @param suffix The estimated suffix of all column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the suffix is {@code null} - */ - public DsvBuilder withEndsWithEstimations(final String suffix) { - if (null == suffix) { - throw new NullPointerException("Invalid suffix (not null expected)"); - } - return withEstimations(value -> value.endsWith(suffix)); - } - - /** - *

Add a {@code String.endsWith()} estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param suffix The estimated suffix of specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the suffix is {@code null} - */ - public DsvBuilder withEndsWithEstimation(final int index, final String suffix) { - if (null == suffix) { - throw new NullPointerException("Invalid suffix (not null expected)"); - } - return withEstimation(index, value -> value.endsWith(suffix)); - } - - /** - *

Add a {@code String.endsWith()} constraint to the column at specified index of the DSV data. In others words - * that means each value of this column should always end with the suffix.

- * - * @param index Index of the column - * @param suffix The required suffix of specified column values - * @return The {@code DsvBuilder} instance - * @throws NullPointerException If the suffix is {@code null} - */ - public DsvBuilder withEndsWithConstraint(final int index, final String suffix) { - if (null == suffix) { - throw new NullPointerException("Invalid suffix (not null expected)"); - } - return withConstraint(index, value -> value.endsWith(suffix)); - } - - // CUSTOM - /** - *

Add custom estimation function to all columns of the DSV data.

- * - * @param function The custom estimation function for all column values - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withEstimations(final Function function) { - IntStream.range(0, nbColumns).forEach(i -> evaluator.addEstimation(i, Estimation.of(function))); - return this; - } - - /** - *

Add a custom estimation function to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param function The custom estimation function for specified column values - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withEstimation(final int index, final Function function) { - return withEstimation(index, Estimation.of(function)); - } - - /** - *

Add a custom estimation to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param estimation The custom estimation for specified column values - * @return The {@code DsvBuilder} instance - * @throws IndexOutOfBoundsException If the index is not valid - */ - public DsvBuilder withEstimation(final int index, final Estimation estimation) { - if (0 > index || nbColumns <= index) { - throw new IndexOutOfBoundsException("Invalid index: " + index + " (between 0 and " + (nbColumns - 1) + " expected)"); - } - evaluator.addEstimation(index, estimation); - return this; - } - - /** - *

Add a custom constraint predicate to all columns of the DSV data.

- * - * @param predicate The custom constraint predicate for all column values - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withConstraints(final Predicate predicate) { - IntStream.range(0, nbColumns).forEach(i -> evaluator.addConstraint(i, Constraint.of(predicate))); - return this; - } - - /** - *

Add a custom constraint predicate to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param predicate The custom constraint predicate for specified column values - * @return The {@code DsvBuilder} instance - */ - public DsvBuilder withConstraint(final int index, final Predicate predicate) { - return withConstraint(index, Constraint.of(predicate)); - } - - /** - *

Add a custom constraint to the column at specified index of the DSV data.

- * - * @param index Index of the column - * @param constraint The custom constraint for specified column values - * @return The {@code DsvBuilder} instance - * @throws IndexOutOfBoundsException If the index is not valid - */ - public DsvBuilder withConstraint(final int index, final Constraint constraint) { - if (0 > index || nbColumns <= index) { - throw new IndexOutOfBoundsException("Invalid index: " + index + " (between 0 and " + (nbColumns - 1) + " expected)"); - } - evaluator.addConstraint(index, constraint); - return this; - } - - /** - *

Build and return a {@code DsvMender} instance using configured attributes.

- * - * @return The {@code DsvMender} instance - */ - public DsvMender build() { - return new DsvMender(evaluator, delimiter, nbColumns, maxDepth); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/DsvEvaluator.java b/src/main/java/org/mender/dsv/DsvEvaluator.java deleted file mode 100644 index f9c80f2..0000000 --- a/src/main/java/org/mender/dsv/DsvEvaluator.java +++ /dev/null @@ -1,148 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.util.HashMap; -import java.util.Map; - -import org.mender.Evaluator; -import org.mender.criteria.Constraint; -import org.mender.criteria.Estimation; - -/** - *

A {@link Evaluator} implementation for the DSV format.

- * - *

Score computation: (1 * c[1] * c2] * c[3]...) * (e[1] + e[2] + e[3]...)
- * (with c[n] the n-th constraint score and e[n] the n-th estimation score)

- * - * @since 1.0 - */ -class DsvEvaluator implements Evaluator { - - /** - *

Map of constraints associated to column indexes.

- */ - private final Map, Integer> constraints = new HashMap<>(); - - /** - *

Map of estimations associated to column indexes.

- */ - private final Map, Integer> estimations = new HashMap<>(); - - /** - *

Add a new {@code Constraint} at the specified index.

- * - * @param index The related column index - * @param constraint The constraint to register - * @throws NullPointerException If the constraint is {@code null} - * @throws IndexOutOfBoundsException If the index is negative - */ - public void addConstraint(final int index, final Constraint constraint) { - if (null == constraint) { - throw new NullPointerException("Invalid constraint (not null expected)"); - } - if (0 > index) { - throw new IndexOutOfBoundsException("Invalid index: " + index + " (greater than or equal to 0 expected)"); - } - constraints.put(constraint, index); - } - - /** - *

Add a new {@code Estimation} at the specified index.

- * - * @param index The related column index - * @param estimation The estimation to register - * @throws NullPointerException If the estimation is {@code null} - * @throws IndexOutOfBoundsException If the index is negative - */ - public void addEstimation(final int index, final Estimation estimation) { - if (null == estimation) { - throw new NullPointerException("Invalid estimation (not null expected)"); - } - if (0 > index) { - throw new IndexOutOfBoundsException("Invalid index: " + index + " (greater than or equal to 0 expected)"); - } - estimations.put(estimation, index); - } - - /** - *

Check constraints using given values.

- * - * @param values Values to use to check - * @return {@code true} if values pass all constraints - * @throws NullPointerException If values are {@code null} - */ - @Override - public boolean checkConstraints(final String[] values) { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - for (final Map.Entry, Integer> constraint : constraints.entrySet()) { - if (!constraint.getKey().check(values[constraint.getValue()])) { - return false; - } - } - return true; - } - - /** - *

Adjust estimations using given values.

- * - * @param values Values to use to adjust - * @throws NullPointerException If values are {@code null} - */ - @Override - public void adjustEstimations(final String[] values) { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - for (final Map.Entry, Integer> estimation : estimations.entrySet()) { - estimation.getKey().adjust(values[estimation.getValue()]); - } - } - - /** - *

Evaluate given values to get a computed score.

- * - * @param values Values to evaluate - * @throws NullPointerException If values are {@code null} - */ - @Override - public double evaluate(final String[] values) { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - return - constraints.entrySet() - .stream() - .mapToDouble(constraint -> constraint.getKey().calculate(values[constraint.getValue()])) - .reduce(1, (a, b) -> a * b) * - ( - estimations.isEmpty() ? 1.0d : estimations.entrySet() - .stream() - .mapToDouble(estimation -> estimation.getKey().calculate(values[estimation.getValue()])) - .sum() - ); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/DsvMender.java b/src/main/java/org/mender/dsv/DsvMender.java deleted file mode 100644 index 6644bee..0000000 --- a/src/main/java/org/mender/dsv/DsvMender.java +++ /dev/null @@ -1,544 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; -import org.mender.Mender; -import org.mender.MenderException; - -/** - *

Main component to use to repair malformed DSV data.

- * - * @since 1.0 - */ -public class DsvMender implements Mender { - - /** - *

{@code Nodes} is an object that wraps and optimizes a collection of {@code String} arrays for - * {@code DsvMender} uses.

- * - *

Optimizations:
- * - Unlike a {@link java.util.HashMap}, the hash of the String array is computed using a custom hash function - * that serves as the key of the map.
- * - Because of large uses of equivalents {@code String} objects, {@code String.intern()} is called for all of - * them.

- * - * @since 1.0 - */ - private static class Nodes { - - /** - *

Delegate map with hash codes as keys and {@code String} arrays as values.

- */ - private final Map delegate = new HashMap<>(); - - /** - *

Add all entries to the {@code Nodes}.

- * - * @param entries Collection of entries to add - */ - void addAll(final Collection entries) { - for (final String[] entry : entries) { - final int hashCode = customHashCode(entry); - if (delegate.containsKey(hashCode)) { - continue; - } - for (int i = 0; i < entry.length; ++i) { - entry[i] = entry[i].intern(); - } - delegate.put(hashCode, entry); - } - } - - /** - *

Return a collection of {@code Nodes} values.

- * - * @return The collection of values - */ - Collection values() { - return delegate.values(); - } - - /** - *

Empty all nodes.

- */ - void clear() { - delegate.clear(); - } - - /** - *

Custom hash code generation of {@code String} arrays, because of a high risk of collision with standard - * {@code Arrays.hashCode} function.

- *

Example of collision:
- * {@code Arrays.hashCode(new String[] {"o", "ooo"})} gives 114625
- * {@code Arrays.hashCode(new String[] {"oo", "oo"})} gives 114625 too

- * - * @param array The array to get the hash code from - * @return Computed hash code - */ - private static int customHashCode(final String[] array) { - int arrayHashCode = 1; - for (int i = 0; i < array.length; ++i) { - final String string = array[i]; - int stringHashCode = 0; - if (null != string) { - final int length = string.length(); - for (int j = 0; j < length; ++j) { - stringHashCode = 257 * stringHashCode + string.charAt(j); - } - } - arrayHashCode = 31 * arrayHashCode + stringHashCode; - } - return arrayHashCode; - } - } - - /** - *

Default maximum depth of the nodes tree.

- */ - static final int DEFAULT_MAX_DEPTH = 20; - - /** - *

{@code DsvEvaluator} object.

- */ - private final DsvEvaluator evaluator; - - /** - *

String delimiter of the DSV data.

- */ - private final String delimiter; - - /** - *

Number of columns of the DSV data.

- */ - private final int nbColumns; - - /** - *

Maximum depth of the nodes tree.

- */ - private final int maxDepth; - - /** - *

Map that associates nodes to their scores.

- */ - private Map nodeScores; - - /** - *

Best score, of the returned node.

- */ - private double score; - - /** - *

Package-private constructor used by the {@link DsvBuilder} class.

- * - * @param evaluator {@code DsvEvaluator} to use - * @param delimiter {@code String} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @param maxDepth Maximum depth of the nodes tree - * @throws NullPointerException If the evaluator or the delimiter are {@code null} - * @throws IllegalArgumentException If the number of columns if lower than 2 - */ - DsvMender(final DsvEvaluator evaluator, final String delimiter, final int nbColumns, final int maxDepth) { - if (null == evaluator) { - throw new NullPointerException("Invalid evaluator (not null expected)"); - } - if (null == delimiter) { - throw new NullPointerException("Invalid delimiter (not null expected)"); - } - if (2 > nbColumns) { - throw new IllegalArgumentException("Invalid number of columns: " + nbColumns + " (greater than or equal to 2 expected)"); - } - this.evaluator = evaluator; - this.delimiter = delimiter; - this.nbColumns = nbColumns; - this.maxDepth = maxDepth; - } - - /** - *

Fit the evaluator with a row from the DSV data, only if it is well-formed.

- * - * @param row Row of separated values - * @throws NullPointerException If the row is {@code null} - */ - public void fitIfValid(final String row) { - if (null == row) { - throw new NullPointerException("Invalid row (not null expected)"); - } - fitIfValid(StringUtils.splitByWholeSeparatorPreserveAllTokens(row, delimiter)); - } - - /** - *

Fit the evaluator with a row from the DSV data, only if it is well-formed.

- * - * @param values Array of separated values - * @throws NullPointerException If values are {@code null} - */ - public void fitIfValid(final String[] values) { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - if (nbColumns == values.length && evaluator.checkConstraints(values)) { - fit(values); - } - } - - /** - *

Fit the evaluator with a well-formed row from the DSV data.

- * - * @param row Row of well-formed separated values - * @throws NullPointerException If the row is {@code null} - */ - public void fit(final String row) { - if (null == row) { - throw new NullPointerException("Invalid row (not null expected)"); - } - fit(StringUtils.splitByWholeSeparatorPreserveAllTokens(row, delimiter)); - } - - /** - *

Fit the evaluator with a well-formed row from the DSV data.

- * - * @param values Array of well-formed separated values - * @throws NullPointerException If values are {@code null} - * @throws IllegalArgumentException If the number of values is different from the number of columns or if values - * don't pass constraints - */ - @Override - public void fit(final String[] values) { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - if (nbColumns != values.length) { - throw new IllegalArgumentException("Invalid number of values: " + values.length + " (" + nbColumns + " expected)"); - } - if (!evaluator.checkConstraints(values)) { - throw new IllegalArgumentException("Invalid values: " + Arrays.toString(values) + " (constraints not passed)"); - } - evaluator.adjustEstimations(values); - } - - /** - *

In some cases you should consider using this method instead of {@code fix()} for better performances and less - * memory consumption when lot of delimiters are appearing consecutively.

- * - * @param row Row of malformed separated values - * @param nbSafeColumns The maximum number of possibly consecutive empty values in a well-formed row - * @return Fixed extracted values from the row - * @throws NullPointerException If the row is {@code null} - * @throws IllegalArgumentException If the number of safe columns is negative - * @throws MenderException In particular cases the {@code fix()} method might not work because of used criteria - * @see DsvUtils - */ - public String[] optimizedFix(final String row, final int nbSafeColumns) throws MenderException { - if (null == row) { - throw new NullPointerException("Invalid row (not null expected)"); - } - return optimizedFix(StringUtils.splitByWholeSeparatorPreserveAllTokens(row, delimiter), nbSafeColumns); - } - - /** - *

In some cases you should consider using this method instead of {@code fix()} for better performances and less - * memory consumption when lot of delimiters are appearing consecutively.

- * - * @param values Array of malformed separated values - * @param nbSafeColumns The maximum number of possibly consecutive empty values in a well-formed row - * @return Fixed values - * @throws NullPointerException If values are {@code null} - * @throws IllegalArgumentException If the number of safe columns is negative - * @throws MenderException In particular cases the {@code fix()} method might not work because of used criteria - * @see DsvUtils - */ - public String[] optimizedFix(final String[] values, final int nbSafeColumns) throws MenderException { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - if (0 > nbSafeColumns) { - throw new IllegalArgumentException("Invalid number of safe columns (greater than or equal to 0 expected)"); - } - return fix(DsvUtils.optimize(values, delimiter, nbColumns, nbSafeColumns)); - } - - /** - *

Fix a row from the DSV data using previously learned ones, only if it is malformed.

- * - * @param row Row of separated values - * @return Fixed extracted values from the row if it was malformed - * @throws MenderException In particular cases that method might not work because of used criteria - * @throws NullPointerException If the row is {@code null} - */ - public String[] fixIfNotValid(final String row) throws MenderException { - if (null == row) { - throw new NullPointerException("Invalid row (not null expected)"); - } - return fixIfNotValid(StringUtils.splitByWholeSeparatorPreserveAllTokens(row, delimiter)); - } - - /** - *

Fix a row from the DSV data using previously learned ones, only if it is malformed.

- * - * @param values Array of separated values - * @return Fixed values if they were malformed - * @throws MenderException In particular cases that method might not work because of used criteria - * @throws NullPointerException If values are {@code null} - */ - public String[] fixIfNotValid(final String[] values) throws MenderException { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - if (nbColumns != values.length) { - return fix(values); - } else { - return values; - } - } - - /** - *

Fix a malformed row from the DSV data using previously learned ones.

- * - * @param row Row of malformed separated values - * @return Fixed extracted values from the row - * @throws MenderException In particular cases that method might not work because of used criteria - * @throws NullPointerException If the row is {@code null} - */ - public String[] fix(final String row) throws MenderException { - if (null == row) { - throw new NullPointerException("Invalid row (not null expected)"); - } - return fix(StringUtils.splitByWholeSeparatorPreserveAllTokens(row, delimiter)); - } - - /** - *

Fix a malformed row from the DSV data using previously learned ones.

- * - * @param values Array of malformed separated values - * @return Fixed values - * @throws MenderException In particular cases that method might not work because of used criteria - * @throws NullPointerException If values are {@code null} - * @throws IllegalArgumentException If the number of values is equal to the number of columns - */ - @Override - public String[] fix(final String[] values) throws MenderException { - if (null == values) { - throw new NullPointerException("Invalid values (not null expected)"); - } - if (nbColumns == values.length) { - throw new IllegalArgumentException("Invalid number of values: " + values.length + " (different from " + nbColumns + " expected)"); - } - if (maxDepth < Math.abs(nbColumns - values.length - 2)) { - throw new MenderException("Could not fix because the depth should be less than or equal to " + maxDepth + " (" + Math.abs(nbColumns - values.length - 2) + " for given values)"); - } - final Nodes nodes = new Nodes(); - if (nbColumns < values.length) { - nodes.addAll(generateJoinChildNodes(values)); - for (int i = nbColumns; i < values.length - 1; ++i) { - final List subNodes = new ArrayList<>(nodes.values()); - nodes.clear(); - for (final String[] subNode : subNodes) { - nodes.addAll(generateJoinChildNodes(subNode)); - } - } - } else { - nodes.addAll(generateShiftChildNodes(values)); - for (int i = nbColumns; i > values.length + 1; --i) { - final List subNodes = new ArrayList<>(nodes.values()); - nodes.clear(); - for (final String[] subNode : subNodes) { - nodes.addAll(generateShiftChildNodes(subNode)); - } - } - } - nodeScores = new HashMap<>(); - for (final String[] node : nodes.values()) { - final double nodeScore = evaluator.evaluate(node); - if (0 < nodeScore) { - nodeScores.put(node, nodeScore); - } - } - if (nodeScores.isEmpty()) { - throw new MenderException("No solution has been found for values: \"" + Arrays.toString(values) + "\" (consider using others estimations or constraints)"); - } - final Map.Entry bestNode = Collections.max(nodeScores.entrySet(), Map.Entry.comparingByValue()); - score = bestNode.getValue(); - return bestNode.getKey(); - } - - /** - *

Create child nodes from a parent by computing all possible merging cases of values. Each value is attempted to - * be merged with the next one using the delimiter as separator.

- * - * @param parent The parent node - * @return List of generated child nodes - */ - private List generateJoinChildNodes(final String[] parent) { - final List children = new ArrayList<>(parent.length - 1); - for (int i = 0; i < parent.length - 1; ++i) { - final String[] child = new String[parent.length - 1]; - for (int j = 0; j < parent.length - 1; ++j) { - if (j == i) { - child[j] = parent[j] + delimiter + parent[j + 1]; - } else { - child[j] = parent[j < i ? j : j + 1]; - } - } - children.add(child); - } - return children; - } - - /** - *

Create child nodes from a parent by computing all possible shifting cases of values. An empty value is added - * at any position between all values.

- * - * @param parent The parent node - * @return List of generated child nodes - */ - private List generateShiftChildNodes(final String[] parent) { - final List children = new ArrayList<>(parent.length + 1); - for (int i = 0; i < parent.length + 1; ++i) { - final String[] child = new String[parent.length + 1]; - for (int j = 0; j < parent.length + 1; ++j) { - if (j == i) { - child[j] = ""; - } else { - child[j] = parent[j < i ? j : j - 1]; - } - } - children.add(child); - } - return children; - } - - /** - *

Return a map that associates scores to each node generated by the last call to {@code fix()}.

- * - * @return An unmodifiable map of nodes' scores - */ - public Map getNodeScores() { - return Collections.unmodifiableMap(nodeScores); - } - - /** - *

Return the score of the more relevant node used by the last call to {@code fix()}.

- * - * @return The more relevant score - */ - public double getScore() { - return score; - } - - /** - *

Create a new {@link DsvBuilder} instance to build a {@code DsvMender} object, using a {@code char} delimiter - * and the default maximum depth.

- * - * @param delimiter {@code char} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @return The instantiated {@link DsvBuilder} instance - */ - public static DsvBuilder builder(final char delimiter, final int nbColumns) { - return builder(Character.toString(delimiter), nbColumns); - } - - /** - *

Create a new {@link DsvBuilder} instance to build a {@code DsvMender} object, using a {@code String} delimiter - * and the default maximum depth.

- * - * @param delimiter {@code String} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @return The instantiated {@link DsvBuilder} instance - */ - public static DsvBuilder builder(final String delimiter, final int nbColumns) { - return builder(delimiter, nbColumns, DEFAULT_MAX_DEPTH); - } - - /** - *

Create a new {@link DsvBuilder} instance to build a {@code DsvMender} object, using a {@code char} delimiter - * and a non-default maximum depth.

- * - * @param delimiter {@code char} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @param maxDepth Maximum depth of the nodes tree - * @return The instantiated {@link DsvBuilder} instance - * @throws NullPointerException If the delimiter is {@code null} - * @throws IllegalArgumentException If the number of columns is lower than 2 - */ - public static DsvBuilder builder(final char delimiter, final int nbColumns, final int maxDepth) { - return builder(Character.toString(delimiter), nbColumns, maxDepth); - } - - /** - *

Create a new {@link DsvBuilder} instance to build a {@code DsvMender} object, using a {@code String} delimiter - * and a non-default maximum depth.

- * - * @param delimiter {@code String} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @param maxDepth Maximum depth of the nodes tree - * @return The instantiated {@link DsvBuilder} instance - * @throws NullPointerException If the delimiter is {@code null} - * @throws IllegalArgumentException If the number of columns is lower than 2 - */ - public static DsvBuilder builder(final String delimiter, final int nbColumns, final int maxDepth) { - if (null == delimiter) { - throw new NullPointerException("Invalid delimiter (not null expected)"); - } - if (0 > nbColumns) { - throw new IllegalArgumentException("Invalid number of columns: " + nbColumns + " (greater than or equal to 2 expected)"); - } - return new DsvBuilder(delimiter, nbColumns, maxDepth); - } - - /** - *

Create a new {@link DsvBuilder} instance automatically, using a {@code char} delimiter and configured with - * empty-string and length-string estimations.

- * - * @param delimiter {@code char} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @return The instantiated {@link DsvBuilder} instance - */ - public static DsvMender auto(final char delimiter, final int nbColumns) { - return auto(Character.toString(delimiter), nbColumns); - } - - /** - *

Create a new {@link DsvBuilder} instance automatically, using a {@code String} delimiter and configured with - * empty-string and length-string estimations.

- * - * @param delimiter {@code String} delimiter of the DSV data - * @param nbColumns Number of columns of the DSV data - * @return The instantiated {@link DsvBuilder} instance - */ - public static DsvMender auto(final String delimiter, final int nbColumns) { - return builder(delimiter, nbColumns, DEFAULT_MAX_DEPTH) - .withEmptyEstimations() - .withLengthEstimations() - .build(); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/DsvReader.java b/src/main/java/org/mender/dsv/DsvReader.java deleted file mode 100644 index aff8df5..0000000 --- a/src/main/java/org/mender/dsv/DsvReader.java +++ /dev/null @@ -1,163 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.io.BufferedReader; -import java.io.Closeable; -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.nio.charset.Charset; -import java.nio.file.Files; -import java.nio.file.Path; - -import org.mender.MenderException; - -/** - *

A reader for DSV data that fit itself progressively while forwarding in the stream using the given - * {@code DsvMender}. Invalids rows are automatically fixed using at-the-time knowledges.

- * - *

Node: For better results you should instead use a two-pass process that first fit valid rows and then fix - * invalids ones.

- * - * @since 1.0 - */ -public class DsvReader implements Closeable { - - /** - *

Configured {@code DsvMender} to use.

- */ - private final DsvMender mender; - - /** - *

Delegated {@code BufferedReader} to read lines from the input.

- */ - private final BufferedReader bufferedReader; - - /** - *

Boolean that indicate if a line has already been read.

- */ - private boolean hasStarted = false; - - /** - *

Constructor using a file {@code Path}.

- * - * @param mender The {@code DSVMender} to use - * @param file The input file {@code Path} - * @throws IOException Might occurs with I/O operations - */ - public DsvReader(final DsvMender mender, final Path file) throws IOException { - this(mender, Files.newBufferedReader(file)); - } - - /** - *

Constructor using a file {@code Path} and a custom {@code Charset}.

- * - * @param mender The {@code DSVMender} to use - * @param file The input file {@code Path} - * @param charset Custom {@code Charset} - * @throws IOException Might occurs with I/O operations - */ - public DsvReader(final DsvMender mender, final Path file, final Charset charset) throws IOException { - this(mender, Files.newBufferedReader(file, charset)); - } - - /** - *

Constructor using a {@code String}

- * - * @param mender The {@code DSVMender} to use - * @param string The {@code String} to use - */ - public DsvReader(final DsvMender mender, final String string) { - this(mender, new StringReader(string)); - } - - /** - *

Constructor using a {@code Reader}

- * - * @param mender The {@code DSVMender} to use - * @param reader The {@code Reader} to use - */ - public DsvReader(final DsvMender mender, final Reader reader) { - this(mender, new BufferedReader(reader)); - } - - /** - *

Constructor using a {@code BufferedReader}

- * - * @param mender The {@code DSVMender} to use - * @param bufferedReader The {@code BufferedReader} to use - * @throws NullPointerException If the {@code DsvMender} or the {@code BufferedReader} are {@code null} - */ - public DsvReader(final DsvMender mender, final BufferedReader bufferedReader) { - if (null == mender) { - throw new NullPointerException("Invalid DSV mender (not null expected)"); - } - if (null == bufferedReader) { - throw new NullPointerException("Invalid buffered reader (not null expected)"); - } - this.mender = mender; - this.bufferedReader = bufferedReader; - } - - /** - *

Read the DSV header, it must be the first read line. Note that no fit operation will be performed.

- * - * @return Array of DSV header values - * @throws MenderException If the header has invalid number of values and if the fix has failed - * @throws IOException Might occurs with I/O operations - */ - public String[] readHeader() throws MenderException, IOException { - if (hasStarted) { - throw new IllegalStateException("Header must be the first read line"); - } - final String line = bufferedReader.readLine(); - if (null == line) { - return null; - } - return mender.fixIfNotValid(line); - } - - /** - *

Read a DSV row, performing a fit operation if it is valid, or a fix operation else.

- * - * @return Array of DSV row values - * @throws MenderException If the row has invalid number of values and if the fix has failed - * @throws IOException Might occurs with I/O operations - */ - public String[] readRow() throws MenderException, IOException { - hasStarted = true; - final String line = bufferedReader.readLine(); - if (null == line) { - return null; - } - mender.fitIfValid(line); - return mender.fixIfNotValid(line); - } - - @Override - public void close() throws IOException { - bufferedReader.close(); - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/DsvUtils.java b/src/main/java/org/mender/dsv/DsvUtils.java deleted file mode 100644 index 6eca79b..0000000 --- a/src/main/java/org/mender/dsv/DsvUtils.java +++ /dev/null @@ -1,106 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.util.Arrays; - -/** - *

Utility class that for now only provides an optimization strategy.

- * - * @since 1.0 - */ -final class DsvUtils { - - /** - *

Constructor not available

- */ - private DsvUtils() { - throw new AssertionError(); - } - - /** - *

This function aims to considerably improve performances of the {@code DsvMender.fix()} method for specials - * cases, especially when the delimiter occurs several times consecutively and lead to nodes explosion.

- * - * @param values Array of values to optimize - * @param delimiter DSV delimiter string - * @param nbColumns DSV number of columns - * @param nbSafeColumns An indication of possibly consecutive empty values - * @return Array of optimized values - */ - static String[] optimize(final String[] values, final String delimiter, final int nbColumns, final int nbSafeColumns) { - String[] result = Arrays.copyOf(values, values.length); - while (nbColumns < result.length) { - // Interval of longest consecutive empty values - int dmin = 0; - int dmax = 0; - int s = -1; - for (int i = 0; i < result.length; ++i) { - if (-1 == s && result[i].isEmpty()) { - s = i; - continue; - } - if (-1 != s && !result[i].isEmpty()) { - if (dmax - dmin < i - s) { - dmin = s; - dmax = i; - } - s = -1; - } - } - if (-1 != s && dmax - dmin < result.length - s) { - dmin = s; - dmax = result.length; - } - if (dmax - dmin > 2 * nbSafeColumns + 1) { - // Merging consecutive empty values while keeping a safe amount on each side - for (int i = dmin + nbSafeColumns + 1; i < dmax - nbSafeColumns; ++i) { - result = remove(result, dmin + nbSafeColumns + 1); - result[dmin + nbSafeColumns] = result[dmin + nbSafeColumns] + delimiter; - } - } else { - break; - } - } - return result; - } - - /** - *

Remove an element from an array at a specific index.

- * - * @param array The array to remove the element from - * @param index Index of the element to remove in the array - * @return A new array without the removed element - */ - private static String[] remove(final String[] array, final int index) { - final String[] result = new String[array.length - 1]; - if (0 < index) { - System.arraycopy(array, 0, result, 0, index); - } - if (index < array.length - 1) { - System.arraycopy(array, index + 1, result, index, array.length - index - 1); - } - return result; - } -} \ No newline at end of file diff --git a/src/main/java/org/mender/dsv/package-info.java b/src/main/java/org/mender/dsv/package-info.java deleted file mode 100644 index 0f23d30..0000000 --- a/src/main/java/org/mender/dsv/package-info.java +++ /dev/null @@ -1,38 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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. -*/ -/** - *

{@code Mender} implementation for malformed DSV data.

- * - *

DSV (Delimiter-Separated Values) is a data format that consists of rows of values separated by a delimiting - * character or a string. CSV (Comma-Separated Values) is a famous and commonly-used format inherited from - * DSV.

- *

While most of that formatted data are escaping the delimiter, that's not always the case. Basically sometimes the - * non-escaped delimiter is contained by some values and while parsing the computer is not able to retrieve the original - * information, or malformed lines are simply ignored. Another case is when values are missing from some lines.

- *

{@link org.mender.dsv.DsvMender} is a tool that is able to repair malformed DSV data by learning columns features - * from corrects rows.

- * - * @since 1.0 - */ -package org.mender.dsv; \ No newline at end of file diff --git a/src/main/java/org/mender/package-info.java b/src/main/java/org/mender/package-info.java deleted file mode 100644 index df19d7f..0000000 --- a/src/main/java/org/mender/package-info.java +++ /dev/null @@ -1,30 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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. -*/ -/** - *

Components of the experimental {@link org.mender.Mender} pattern that have to be implemented by concrete - * classes.

- * - * @since 1.0 - */ -package org.mender; \ No newline at end of file diff --git a/src/main/resources/missing_cells.tsv b/src/main/resources/missing_cells.tsv deleted file mode 100644 index f05a291..0000000 --- a/src/main/resources/missing_cells.tsv +++ /dev/null @@ -1,5 +0,0 @@ -YEAR MAKE MODEL DESCRIPTION PRICE -1997 Ford E350 ac, abs, moon 3000.00 -1999 Venture "Extended Edition" 4900.00 -1999 Chevy Venture "Extended Edition", Very Large 5000.00 -1996 Jeep Grand Cherokee MUST SELL! air, moon roof, loaded 4799.00 \ No newline at end of file diff --git a/src/main/resources/not_quoted.csv b/src/main/resources/not_quoted.csv deleted file mode 100644 index 5ab3bdb..0000000 --- a/src/main/resources/not_quoted.csv +++ /dev/null @@ -1,6 +0,0 @@ -ID,NAME,DESCRIPTION,BIRTHDAY,COUNTRY -1,John,Hey everyone I'm the first user,1984-05-16,United Kingdom -2,Pierre,Bonjour à tous vous allez bien ?,1992-11-26,France -3,Pedro,Holà qué tal ?,1962-01-05,Spain -4,Arnold,My country name contains a , in it,1974-05-30,Macedonia, Rep. of -5,Peter,I, like, to, use, commas, between, words,1994-12-04,United States \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/api/MendExceptionTest.java b/src/test/java/com/github/alexisjehan/mender/api/MendExceptionTest.java new file mode 100644 index 0000000..f7023ed --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/api/MendExceptionTest.java @@ -0,0 +1,48 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api; + +import com.github.alexisjehan.javanilla.io.Serializables; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link MendException} unit tests.

+ */ +final class MendExceptionTest { + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new MendException(null)); + } + + @Test + void testSerializable() { + final var mendException = new MendException("foo"); + final var deserializedMendException = Serializables.deserialize(Serializables.serialize(mendException)); + assertThat(deserializedMendException.getMessage()).isEqualTo(mendException.getMessage()); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluatorTest.java b/src/test/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluatorTest.java new file mode 100644 index 0000000..87844de --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/api/evaluators/ConstraintEvaluatorTest.java @@ -0,0 +1,54 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api.evaluators; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link ConstraintEvaluator} unit tests.

+ */ +final class ConstraintEvaluatorTest { + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new ConstraintEvaluator<>(null)); + } + + @Test + void testIsValid() { + final var evaluator = new ConstraintEvaluator<>("foo"::equals); + assertThat(evaluator.isValid("foo")).isTrue(); + assertThat(evaluator.isValid("bar")).isFalse(); + } + + @Test + void testEvaluate() { + final var evaluator = new ConstraintEvaluator<>("foo"::equals); + assertThat(evaluator.evaluate("foo")).isEqualTo(1.0d); + assertThat(evaluator.evaluate("bar")).isNaN(); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluatorTest.java b/src/test/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluatorTest.java new file mode 100644 index 0000000..bd80753 --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/api/evaluators/EstimationEvaluatorTest.java @@ -0,0 +1,68 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.api.evaluators; + +import org.junit.jupiter.api.Test; + +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link EstimationEvaluator} unit tests.

+ */ +final class EstimationEvaluatorTest { + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new EstimationEvaluator<>(null)); + } + + @Test + void testFitEvaluate() { + { + final var evaluator = new EstimationEvaluator<>(Function.identity()); + assertThat(evaluator.evaluate("foo")).isNaN(); + assertThat(evaluator.evaluate("bar")).isNaN(); + evaluator.fit("foo"); + assertThat(evaluator.evaluate("foo")).isEqualTo(1.0d); + assertThat(evaluator.evaluate("bar")).isEqualTo(0.0d); + evaluator.fit("bar"); + assertThat(evaluator.evaluate("foo")).isEqualTo(0.5d); + assertThat(evaluator.evaluate("bar")).isEqualTo(0.5d); + } + { + final var evaluator = new EstimationEvaluator<>(String::length); + assertThat(evaluator.evaluate("foo")).isNaN(); + assertThat(evaluator.evaluate("fooo")).isNaN(); + evaluator.fit("foo"); + assertThat(evaluator.evaluate("foo")).isEqualTo(1.0d); + assertThat(evaluator.evaluate("fooo")).isEqualTo(0.0d); + evaluator.fit("fooo"); + assertThat(evaluator.evaluate("foo")).isEqualTo(0.5d); + assertThat(evaluator.evaluate("fooo")).isEqualTo(0.5d); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendCandidateTest.java b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendCandidateTest.java new file mode 100644 index 0000000..800dbe6 --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendCandidateTest.java @@ -0,0 +1,93 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.javanilla.lang.array.ObjectArrays; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link DsvMendCandidate} unit tests.

+ */ +final class DsvMendCandidateTest { + + @Test + void testConstructorImmutable() { + final var values = ObjectArrays.singleton("foo"); + final var mendCandidate = new DsvMendCandidate(values, 1.0d); + assertThat(mendCandidate.getValue()).containsExactly("foo"); + values[0] = "bar"; + assertThat(mendCandidate.getValue()).containsExactly("foo"); + } + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new DsvMendCandidate(null, 1.0d)); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMendCandidate(ObjectArrays.empty(String.class), 1.0d)); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMendCandidate(ObjectArrays.singleton("foo"), -1.0d)); + } + + @Test + void testEqualsHashCodeToString() { + final var mendCandidate = new DsvMendCandidate(ObjectArrays.singleton("foo"), 1.0d); + assertThat(mendCandidate).isEqualTo(mendCandidate); + assertThat(mendCandidate).isNotEqualTo(1); + { + final var otherMendCandidate = new DsvMendCandidate(mendCandidate.getValue(), mendCandidate.getScore()); + assertThat(mendCandidate).isEqualTo(otherMendCandidate); + assertThat(mendCandidate).hasSameHashCodeAs(otherMendCandidate); + assertThat(mendCandidate).hasToString(otherMendCandidate.toString()); + } + { + final var otherMendCandidate = new DsvMendCandidate(ObjectArrays.singleton("bar"), mendCandidate.getScore()); + assertThat(mendCandidate).isNotEqualTo(otherMendCandidate); + assertThat(mendCandidate.hashCode()).isNotEqualTo(otherMendCandidate.hashCode()); + assertThat(mendCandidate.toString()).isNotEqualTo(otherMendCandidate.toString()); + } + { + final var otherMendCandidate = new DsvMendCandidate(mendCandidate.getValue(), 0.5d); + assertThat(mendCandidate).isNotEqualTo(otherMendCandidate); + assertThat(mendCandidate.hashCode()).isNotEqualTo(otherMendCandidate.hashCode()); + assertThat(mendCandidate.toString()).isNotEqualTo(otherMendCandidate.toString()); + } + } + + @Test + void testGetters() { + final var mendCandidate = new DsvMendCandidate(ObjectArrays.singleton("foo"), 1.0d); + assertThat(mendCandidate.getValue()).containsExactly("foo"); + assertThat(mendCandidate.getScore()).isEqualTo(1.0d); + } + + @Test + void testGettersImmutable() { + final var mendCandidate = new DsvMendCandidate(ObjectArrays.singleton("foo"), 1.0d); + assertThat(mendCandidate.getValue()).containsExactly("foo"); + mendCandidate.getValue()[0] = "bar"; + assertThat(mendCandidate.getValue()).containsExactly("foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendResultTest.java b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendResultTest.java new file mode 100644 index 0000000..0b9579e --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMendResultTest.java @@ -0,0 +1,106 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.javanilla.lang.array.ObjectArrays; +import org.junit.jupiter.api.Test; + +import java.util.HashSet; +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link DsvMendResult} unit tests.

+ */ +final class DsvMendResultTest { + + @Test + void testConstructorImmutable() { + final var values = ObjectArrays.singleton("foo"); + final var mendResult = new DsvMendResult(values, Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + assertThat(mendResult.getValue()).containsExactly("foo"); + values[0] = "bar"; + assertThat(mendResult.getValue()).containsExactly("foo"); + } + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new DsvMendResult(null, Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d))); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMendResult(ObjectArrays.empty(String.class), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d))); + assertThatNullPointerException().isThrownBy(() -> new DsvMendResult(ObjectArrays.singleton("foo"), null, new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d))); + assertThatNullPointerException().isThrownBy(() -> new DsvMendResult(ObjectArrays.singleton("foo"), new HashSet<>(null), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d))); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMendResult(ObjectArrays.singleton("foo"), Set.of(), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d))); + assertThatNullPointerException().isThrownBy(() -> new DsvMendResult(ObjectArrays.singleton("foo"), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), null)); + } + + @Test + void testEqualsHashCodeToString() { + final var mendResult = new DsvMendResult(ObjectArrays.singleton("foo"), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + assertThat(mendResult).isEqualTo(mendResult); + assertThat(mendResult).isNotEqualTo(1); + { + final var otherMendResult = new DsvMendResult(mendResult.getValue(), mendResult.getCandidates(), mendResult.getBestCandidate()); + assertThat(mendResult).isEqualTo(otherMendResult); + assertThat(mendResult).hasSameHashCodeAs(otherMendResult); + assertThat(mendResult).hasToString(otherMendResult.toString()); + } + { + final var otherMendResult = new DsvMendResult(ObjectArrays.singleton("bar"), mendResult.getCandidates(), mendResult.getBestCandidate()); + assertThat(mendResult).isNotEqualTo(otherMendResult); + assertThat(mendResult.hashCode()).isNotEqualTo(otherMendResult.hashCode()); + assertThat(mendResult.toString()).isNotEqualTo(otherMendResult.toString()); + } + { + final var otherMendResult = new DsvMendResult(mendResult.getValue(), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d), new DsvMendCandidate(ObjectArrays.singleton("bar"), 1.0d)), mendResult.getBestCandidate()); + assertThat(mendResult).isNotEqualTo(otherMendResult); + assertThat(mendResult.hashCode()).isNotEqualTo(otherMendResult.hashCode()); + assertThat(mendResult.toString()).isNotEqualTo(otherMendResult.toString()); + } + { + final var otherMendResult = new DsvMendResult(mendResult.getValue(), mendResult.getCandidates(), new DsvMendCandidate(ObjectArrays.singleton("bar"), 1.0d)); + assertThat(mendResult).isNotEqualTo(otherMendResult); + assertThat(mendResult.hashCode()).isNotEqualTo(otherMendResult.hashCode()); + assertThat(mendResult.toString()).isNotEqualTo(otherMendResult.toString()); + } + } + + @Test + void testGetters() { + final var mendResult = new DsvMendResult(ObjectArrays.singleton("foo"), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + assertThat(mendResult.getValue()).containsExactly("foo"); + assertThat(mendResult.getCandidates()).containsExactly(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + assertThat(mendResult.getBestCandidate()).isEqualTo(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + } + + @Test + void testGettersImmutable() { + final var mendResult = new DsvMendResult(ObjectArrays.singleton("foo"), Set.of(new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)), new DsvMendCandidate(ObjectArrays.singleton("bar"), 0.5d)); + assertThat(mendResult.getValue()).containsExactly("foo"); + mendResult.getValue()[0] = "bar"; + assertThat(mendResult.getValue()).containsExactly("foo"); + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderBuilderTest.java b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderBuilderTest.java new file mode 100644 index 0000000..4844fc1 --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderBuilderTest.java @@ -0,0 +1,216 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.javanilla.lang.array.IntArrays; +import com.github.alexisjehan.javanilla.lang.array.ObjectArrays; +import org.junit.jupiter.api.Test; + +import java.util.function.Function; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link DsvMender.Builder} unit tests.

+ */ +final class DsvMenderBuilderTest { + + @Test + void testDefaultMaxDepth() { + final var dsvMender = DsvMender.builder() + .withDelimiter(",") + .withLength(2) + .build(); + assertThat(dsvMender.getMaxDepth()).isEqualTo(20); + } + + @Test + void testWithDelimiter() { + { + final var delimiterStep = DsvMender.builder(); + final var lengthStep = delimiterStep.withDelimiter(','); + assertThat(lengthStep).isSameAs(delimiterStep); + final var buildStep = lengthStep.withLength(2); + final var dsvMender = buildStep.build(); + assertThat(dsvMender.getDelimiter()).isEqualTo(","); + } + final var delimiterStep = DsvMender.builder(); + final var lengthStep = delimiterStep.withDelimiter(","); + assertThat(lengthStep).isSameAs(delimiterStep); + final var buildStep = lengthStep.withLength(2); + final var dsvMender = buildStep.build(); + assertThat(dsvMender.getDelimiter()).isEqualTo(","); + } + + @Test + void testWithLength() { + final var lengthStep = DsvMender.builder() + .withDelimiter(","); + final var buildStep = lengthStep.withLength(2); + assertThat(buildStep).isSameAs(lengthStep); + final var dsvMender = buildStep.build(); + assertThat(dsvMender.getLength()).isEqualTo(2); + } + + @Test + void testWithMaxDepth() { + final var optionalMaxDepthStep = DsvMender.builder() + .withDelimiter(",") + .withLength(2); + final var buildStep = optionalMaxDepthStep.withMaxDepth(1); + assertThat(buildStep).isSameAs(optionalMaxDepthStep); + final var dsvMender = buildStep.build(); + assertThat(dsvMender.getMaxDepth()).isEqualTo(1); + } + + @Test + void testWithConstraint() { + final var optionalEvaluatorStep = DsvMender.builder() + .withDelimiter(",") + .withLength(2); + final var buildStep = optionalEvaluatorStep.withConstraint("foo"::equals); + assertThat(buildStep).isSameAs(optionalEvaluatorStep); + final var dsvMender = buildStep.build(); + final var constraintEvaluators = dsvMender.getConstraintEvaluators(); + assertThat(constraintEvaluators).hasSize(dsvMender.getLength()); + for (final var constraintEvaluator : constraintEvaluators) { + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(1.0d); + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isNaN(); + } + } + + @Test + void testWithConstraintIndexes() { + final var optionalEvaluatorStep = DsvMender.builder() + .withDelimiter(",") + .withLength(2); + final var buildStep = optionalEvaluatorStep.withConstraint("foo"::equals, 0); + assertThat(buildStep).isSameAs(optionalEvaluatorStep); + final var dsvMender = buildStep.build(); + final var constraintEvaluators = dsvMender.getConstraintEvaluators(); + assertThat(constraintEvaluators).hasSize(1); + for (final var constraintEvaluator : constraintEvaluators) { + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(1.0d); + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("foo", "bar"))).isEqualTo(1.0d); + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("bar", "foo"))).isNaN(); + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isNaN(); + } + } + + @Test + void testWithConstraintInvalid() { + final var delimiterStep = DsvMender.builder(); + final var lengthStep = delimiterStep.withDelimiter(","); + final var optionalEvaluatorStep = lengthStep.withLength(2); + assertThatNullPointerException().isThrownBy(() -> optionalEvaluatorStep.withConstraint(null)); + assertThatNullPointerException().isThrownBy(() -> optionalEvaluatorStep.withConstraint("foo"::equals, (int[]) null)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withConstraint("foo"::equals, IntArrays.EMPTY)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withConstraint("foo"::equals, -1)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withConstraint("foo"::equals, 2)); + } + + @Test + void testWithEstimation() { + final var optionalEvaluatorStep = DsvMender.builder() + .withDelimiter(",") + .withLength(2); + final var buildStep = optionalEvaluatorStep.withEstimation(Function.identity()); + assertThat(buildStep).isSameAs(optionalEvaluatorStep); + final var dsvMender = buildStep.build(); + final var estimationEvaluators = dsvMender.getEstimationEvaluators(); + assertThat(estimationEvaluators).hasSize(dsvMender.getLength()); + for (final var estimationEvaluator : estimationEvaluators) { + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isNaN(); + estimationEvaluator.fit(ObjectArrays.of("foo", "foo")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(1.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isEqualTo(0.0d); + estimationEvaluator.fit(ObjectArrays.of("bar", "bar")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(0.5d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isEqualTo(0.5d); + } + } + + @Test + void testWithEstimationIndexes() { + final var optionalEvaluatorStep = DsvMender.builder() + .withDelimiter(",") + .withLength(2); + final var buildStep = optionalEvaluatorStep.withEstimation(Function.identity(), 0); + assertThat(buildStep).isSameAs(optionalEvaluatorStep); + final var dsvMender = buildStep.build(); + final var estimationEvaluators = dsvMender.getEstimationEvaluators(); + assertThat(estimationEvaluators).hasSize(1); + for (final var estimationEvaluator : estimationEvaluators) { + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "bar"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "foo"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isNaN(); + estimationEvaluator.fit(ObjectArrays.of("foo", "foo")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(1.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "bar"))).isEqualTo(1.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "foo"))).isEqualTo(0.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isEqualTo(0.0d); + estimationEvaluator.fit(ObjectArrays.of("bar", "bar")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(0.5d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "bar"))).isEqualTo(0.5d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "foo"))).isEqualTo(0.5d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("bar", "bar"))).isEqualTo(0.5d); + } + } + + @Test + void testWithEstimationInvalid() { + final var delimiterStep = DsvMender.builder(); + final var lengthStep = delimiterStep.withDelimiter(","); + final var optionalEvaluatorStep = lengthStep.withLength(2); + assertThatNullPointerException().isThrownBy(() -> optionalEvaluatorStep.withEstimation(null)); + assertThatNullPointerException().isThrownBy(() -> optionalEvaluatorStep.withEstimation(Function.identity(), (int[]) null)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withEstimation(Function.identity(), IntArrays.EMPTY)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withEstimation(Function.identity(), -1)); + assertThatIllegalArgumentException().isThrownBy(() -> optionalEvaluatorStep.withEstimation(Function.identity(), 2)); + } + + @Test + void testBasic() { + { + final var dsvMender = DsvMender.basic(',', 2); + assertThat(dsvMender.getDelimiter()).isEqualTo(","); + } + final var dsvMender = DsvMender.basic(",", 2); + assertThat(dsvMender.getDelimiter()).isEqualTo(","); + assertThat(dsvMender.getLength()).isEqualTo(2); + final var estimationEvaluators = dsvMender.getEstimationEvaluators(); + assertThat(estimationEvaluators).hasSize(2 * dsvMender.getLength()); + for (final var estimationEvaluator : estimationEvaluators) { + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("", ""))).isNaN(); + estimationEvaluator.fit(ObjectArrays.of("foo", "foo")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo"))).isEqualTo(1.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("", ""))).isEqualTo(0.0d); + } + } +} \ No newline at end of file diff --git a/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderTest.java b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderTest.java new file mode 100644 index 0000000..d447531 --- /dev/null +++ b/src/test/java/com/github/alexisjehan/mender/dsv/DsvMenderTest.java @@ -0,0 +1,153 @@ +/* + * MIT License + * + * Copyright (c) 2017-2019 Alexis Jehan + * + * 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 com.github.alexisjehan.mender.dsv; + +import com.github.alexisjehan.javanilla.lang.Strings; +import com.github.alexisjehan.javanilla.lang.array.ObjectArrays; +import com.github.alexisjehan.mender.api.MendException; +import com.github.alexisjehan.mender.api.evaluators.ConstraintEvaluator; +import com.github.alexisjehan.mender.api.evaluators.EstimationEvaluator; +import org.junit.jupiter.api.Test; + +import java.util.Set; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +/** + *

{@link DsvMender} unit tests.

+ */ +final class DsvMenderTest { + + @Test + void testConstructorInvalid() { + assertThatNullPointerException().isThrownBy(() -> new DsvMender(null, 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMender(Strings.EMPTY, 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMender(",", 1, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatIllegalArgumentException().isThrownBy(() -> new DsvMender(",", 3, 0, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatNullPointerException().isThrownBy(() -> new DsvMender(",", 3, 5, null, Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatNullPointerException().isThrownBy(() -> new DsvMender(",", 3, 5, Set.of((ConstraintEvaluator) null), Set.of(new EstimationEvaluator<>(values -> values[2])))); + assertThatNullPointerException().isThrownBy(() -> new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), null)); + assertThatNullPointerException().isThrownBy(() -> new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of((EstimationEvaluator) null))); + } + + @Test + void testOptimize() { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThat(dsvMender.optimize(0, "foo,")).containsExactly("foo", ""); + assertThat(dsvMender.optimize(0, "foo", "")).containsExactly("foo", ""); + assertThat(dsvMender.optimize(0, "foo", "bar", "", "")).containsExactly("foo", "bar", ","); + assertThat(dsvMender.optimize(0, "", "foo")).containsExactly("", "foo"); + assertThat(dsvMender.optimize(0, "", "", "bar", "foo")).containsExactly(",", "bar", "foo"); + assertThat(dsvMender.optimize(0, "foo", "", "", "bar", "")).containsExactly("foo", ",", "bar", ""); + assertThat(dsvMender.optimize(0, "foo", "bar")).containsExactly("foo", "bar"); + assertThat(dsvMender.optimize(0, "foo", "", "bar")).containsExactly("foo", "", "bar"); + assertThat(dsvMender.optimize(0, "foo", "", "", "", "", "bar")).containsExactly("foo", ",,,", "bar"); + assertThat(dsvMender.optimize(1, "foo", "", "", "", "", "bar")).containsExactly("foo", "", ",", "", "bar"); + assertThat(dsvMender.optimize(2, "foo", "", "", "", "", "bar")).containsExactly("foo", "", "", "", "", "bar"); + } + + @Test + void testOptimizeInvalid() { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThatIllegalArgumentException().isThrownBy(() -> dsvMender.optimize(-1, "foo", "", "", "", "bar")); + assertThatNullPointerException().isThrownBy(() -> dsvMender.optimize(0, (String) null)); + assertThatNullPointerException().isThrownBy(() -> dsvMender.optimize(0, (String[]) null)); + } + + @Test + void testMend() { + { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThat(dsvMender.mend("foo,,bar")).containsExactly("foo", "", "bar"); + assertThat(dsvMender.mend("foo", "", "bar")).containsExactly("foo", "", "bar"); + assertThat(dsvMender.mend("foo")).containsExactly("foo", "", ""); + assertThat(dsvMender.mend("foo", "", "", "", "bar")).containsExactly("foo", ",,", "bar"); + assertThatExceptionOfType(MendException.class).isThrownBy(() -> dsvMender.mend("bar", "", "foo")); + } + { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(), Set.of()); + assertThatExceptionOfType(MendException.class).isThrownBy(() -> dsvMender.mend("foo", "bar")); + } + { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(), Set.of(new EstimationEvaluator<>(values -> values[0].length()), new EstimationEvaluator<>(values -> values[1].length()), new EstimationEvaluator<>(values -> values[2].length()))); + assertThat(dsvMender.mend("foo", "", "bar")).containsExactly("foo", "", "bar"); + assertThat(dsvMender.mend("f", "o", "", "b", "r")).containsExactly("f,o", "", "b,r"); + } + } + + @Test + void testMendInvalid() { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThatNullPointerException().isThrownBy(() -> dsvMender.mend((String) null)); + assertThatNullPointerException().isThrownBy(() -> dsvMender.mend((String[]) null)); + } + + @Test + void testGetLastResult() { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThat(dsvMender.getLastResult()).isEmpty(); + assertThat(dsvMender.mend("foo", "", "bar")).containsExactly("foo", "", "bar"); + assertThat(dsvMender.getLastResult()).isEmpty(); + assertThat(dsvMender.mend("foo", "bar")).containsExactly("foo", "", "bar"); + final var optionalLastResult = dsvMender.getLastResult(); + assertThat(optionalLastResult).isPresent(); + final var lastResult = optionalLastResult.orElseThrow(); + assertThat(lastResult.getValue()).containsExactly("foo", "bar"); + assertThat(lastResult.getCandidates()).containsExactlyInAnyOrder( + new DsvMendCandidate(ObjectArrays.of("", "foo", "bar"), Double.NaN), + new DsvMendCandidate(ObjectArrays.of("foo", "", "bar"), 1.0d), + new DsvMendCandidate(ObjectArrays.of("foo", "bar", ""), 0.5d) + ); + assertThat(lastResult.getBestCandidate()).isEqualTo(new DsvMendCandidate(ObjectArrays.of("foo", "", "bar"), 1.0d)); + } + + @Test + void testGetters() { + final var dsvMender = new DsvMender(",", 3, 5, Set.of(new ConstraintEvaluator<>(values -> "foo".equals(values[0]))), Set.of(new EstimationEvaluator<>(values -> values[2]))); + assertThat(dsvMender.getDelimiter()).isEqualTo(","); + assertThat(dsvMender.getLength()).isEqualTo(3); + assertThat(dsvMender.getMaxDepth()).isEqualTo(5); + final var constraintEvaluators = dsvMender.getConstraintEvaluators(); + assertThat(constraintEvaluators).hasSize(1); + for (final var constraintEvaluator : constraintEvaluators) { + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("foo", "foo", "foo"))).isEqualTo(1.0d); + assertThat(constraintEvaluator.evaluate(ObjectArrays.of("bar", "foo", "foo"))).isNaN(); + } + final var estimationEvaluators = dsvMender.getEstimationEvaluators(); + assertThat(estimationEvaluators).hasSize(1); + for (final var estimationEvaluator : estimationEvaluators) { + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "foo"))).isNaN(); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "foo"))).isNaN(); + estimationEvaluator.fit(ObjectArrays.of("foo", "foo", "foo")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "foo"))).isEqualTo(1.0d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "bar"))).isEqualTo(0.0d); + estimationEvaluator.fit(ObjectArrays.of("foo", "foo", "bar")); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "foo"))).isEqualTo(0.5d); + assertThat(estimationEvaluator.evaluate(ObjectArrays.of("foo", "foo", "bar"))).isEqualTo(0.5d); + } + } +} \ No newline at end of file diff --git a/src/test/java/org/mender/criteria/ConstraintTest.java b/src/test/java/org/mender/criteria/ConstraintTest.java deleted file mode 100644 index 43c3955..0000000 --- a/src/test/java/org/mender/criteria/ConstraintTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.criteria; - -import org.junit.Assert; -import org.junit.Test; - -/** - *

{@link Constraint} unit tests.

- */ -public class ConstraintTest { - - @Test - public void testSimple() { - final Constraint constraint = new Constraint<>("foo"::equals); - Assert.assertEquals(1.0d, constraint.calculate("foo"), 0); - Assert.assertEquals(0.0d, constraint.calculate("bar"), 0); - } - - @Test - public void testStatic() { - final Constraint constraint = Constraint.of("foo"::equals); - Assert.assertEquals(1.0d, constraint.calculate("foo"), 0); - Assert.assertEquals(0.0d, constraint.calculate("bar"), 0); - } - - @Test - public void testCheck() { - final Constraint constraint = new Constraint<>("foo"::equals); - Assert.assertTrue(constraint.check("foo")); - Assert.assertFalse(constraint.check("bar")); - } - - @Test(expected = NullPointerException.class) - public void testNull() { - new Constraint<>(null); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/criteria/EstimationTest.java b/src/test/java/org/mender/criteria/EstimationTest.java deleted file mode 100644 index 367d036..0000000 --- a/src/test/java/org/mender/criteria/EstimationTest.java +++ /dev/null @@ -1,87 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.criteria; - -import org.junit.Assert; -import org.junit.Test; - -/** - *

{@link Estimation} unit tests.

- */ -public class EstimationTest { - - @Test - public void testSimple() { - final Estimation estimation = new Estimation<>(value -> value); - estimation.adjust("foo"); - estimation.adjust("foo"); - estimation.adjust("foo"); - estimation.adjust("bar"); - Assert.assertEquals(0.75d, estimation.calculate("foo"), 0); - Assert.assertEquals(0.25d, estimation.calculate("bar"), 0); - } - - @Test - public void testStatic() { - final Estimation estimation = Estimation.of(value -> value); - estimation.adjust("foo"); - estimation.adjust("foo"); - estimation.adjust("foo"); - estimation.adjust("bar"); - Assert.assertEquals(0.75d, estimation.calculate("foo"), 0); - Assert.assertEquals(0.25d, estimation.calculate("bar"), 0); - } - - @Test(expected = NullPointerException.class) - public void testNull() { - new Estimation<>(null); - } - - @Test - public void testNotPresent() { - final Estimation estimation = new Estimation<>(value -> value); - estimation.adjust("foo"); - estimation.adjust("foo"); - Assert.assertEquals(0.0d, estimation.calculate("bar"), 0); - } - - @Test - public void testEmpty() { - final Estimation estimation = new Estimation<>(value -> value); - Assert.assertEquals(0.0d, estimation.calculate("foo"), 0); - } - - @Test - public void testTransform() { - final Estimation estimation = new Estimation<>(String::length); - estimation.adjust("fooo"); - estimation.adjust("fooo"); - estimation.adjust("foo"); - estimation.adjust("bar"); - Assert.assertEquals(0.0d, estimation.calculate("fo"), 0); - Assert.assertEquals(0.5d, estimation.calculate("foo"), 0); - Assert.assertEquals(0.5d, estimation.calculate("fooo"), 0); - Assert.assertEquals(0.5d, estimation.calculate("bar"), 0); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/dsv/DsvBuilderTest.java b/src/test/java/org/mender/dsv/DsvBuilderTest.java deleted file mode 100644 index 04abaf8..0000000 --- a/src/test/java/org/mender/dsv/DsvBuilderTest.java +++ /dev/null @@ -1,938 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.util.regex.Pattern; - -import org.junit.Assert; -import org.junit.Test; -import org.mender.MenderException; - -/** - *

{@link DsvBuilder} unit tests.

- */ -public class DsvBuilderTest { - - @Test - public void testEmptyEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withEmptyEstimations() - .build(); - mender.fit("a,b,c"); - mender.fit("a,,c"); - mender.fit("a,,c"); - try { - final String[] values = mender.fix("a,,,"); - Assert.assertEquals("a", values[0]); - Assert.assertEquals( "", values[1]); // Has been empty two times - Assert.assertEquals(",", values[2]); // Has not been empty - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testEmptyEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withEmptyEstimation(0) - .withEmptyEstimation(2) - .build(); - mender.fit(",,"); - mender.fit(",,"); - try { - final String[] values = mender.fix(",,,,,,"); - Assert.assertTrue(values[0].isEmpty()); // Has been empty all times - Assert.assertFalse(values[1].isEmpty()); - Assert.assertTrue(values[2].isEmpty()); // Has been empty all times - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testEmptyConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withEmptyConstraint(0) - .build(); - try { - final String[] values = mender.fix(",a,b,c"); - Assert.assertTrue(values[0].isEmpty()); // Must be empty - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = MenderException.class) - public void testEmptyConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withEmptyConstraint(0) - .build(); - mender.fix("a,b,c,"); // No solution - } - - @Test(expected = MenderException.class) - public void testEmptyConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withEmptyConstraint(0) - .withNonEmptyConstraint(0) - .build(); - mender.fix("a,b,c,"); // Conflict - } - - @Test - public void testNonEmptyConstraints() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .build(); - try { - final String[] values = mender.fix(",a,,,c,"); - Assert.assertFalse(values[0].isEmpty()); - Assert.assertFalse(values[1].isEmpty()); - Assert.assertFalse(values[2].isEmpty()); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = MenderException.class) - public void testNonEmptyConstraintsNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .build(); - mender.fix(",,,"); // No solution - } - - @Test - public void testNonEmptyConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraint(2) - .build(); - try { - final String[] values = mender.fix("a,,,c"); - Assert.assertFalse(values[2].isEmpty()); // Must not be empty - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = MenderException.class) - public void testNonEmptyConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraint(1) - .withNonEmptyConstraint(2) - .build(); - mender.fix("a,,,"); // No solution - } - - @Test - public void testLengthEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthEstimations() - .build(); - mender.fit("a,bb,ccc"); - mender.fit("a,bb,ccc"); - mender.fit("a,b,ccc"); - try { - final String[] values = mender.fix("a,ccc"); - Assert.assertEquals( "a", values[0]); // Always had a length of 1 - Assert.assertEquals( "", values[1]); - Assert.assertEquals("ccc", values[2]); // Always had a length of 3 - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testLengthEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthEstimation(1) - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aa,bb,cc"); - mender.fit("a,bb,ccc"); - try { - final String[] values = mender.fix("bb"); - Assert.assertEquals( "", values[0]); - Assert.assertEquals("bb", values[1]); // Always had a length of 2 - Assert.assertEquals( "", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testLengthConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthConstraint(1, 2) - .build(); - try { - final String[] values = mender.fix("aa,,bb,cc"); - Assert.assertEquals("aa,", values[0]); - Assert.assertEquals( "bb", values[1]); // Must have a length of 2 - Assert.assertEquals( "cc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testLengthConstraintInvalid() { - DsvMender.builder(",", 3) - .withLengthConstraint(1, -1) - .build(); - } - - @Test(expected = MenderException.class) - public void testLengthConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthConstraint(1, 2) - .build(); - mender.fix(",a,bbb,cc"); // No solution - } - - @Test(expected = MenderException.class) - public void testLengthConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthConstraint(1, 2) - .withLengthConstraint(1, 3) - .build(); - mender.fix("a,bb,,cc"); // Conflict - } - - @Test - public void testMinLengthEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withMinLengthEstimations(2) - .build(); - mender.fit("a,bb,ccc"); - mender.fit("a,bb,ccc"); - mender.fit("a,b,ccc"); - try { - final String[] values = mender.fix("a,,b,,c"); - Assert.assertEquals( "a", values[0]); // Always had a length less than 2 - Assert.assertEquals(",b", values[1]); - Assert.assertEquals(",c", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMinLengthEstimationsInvalid() { - DsvMender.builder(",", 3) - .withMinLengthEstimations(0) - .build(); - } - - @Test - public void testMinLengthEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withMinLengthEstimation(1, 2) - .build(); - mender.fit("a,bb,ccc"); - mender.fit("a,bbb,ccc"); - mender.fit("aa,b,ccc"); - try { - final String[] values = mender.fix("a,b,,ccc"); - Assert.assertEquals( "a", values[0]); - Assert.assertEquals( "b,", values[1]); // Had a length greater than or equal to 2 most of times - Assert.assertEquals("ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMinLengthEstimationInvalid() { - DsvMender.builder(",", 3) - .withMinLengthEstimation(1, 0) - .build(); - } - - @Test - public void testMinLengthConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withMinLengthConstraint(1, 2) - .build(); - try { - final String[] values = mender.fix(",,b,cc"); - Assert.assertEquals( "", values[0]); - Assert.assertEquals(",b", values[1]); // Must have a length greater than or equal to 2 - Assert.assertEquals("cc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMinLengthConstraintInvalid() { - DsvMender.builder(",", 3) - .withMinLengthConstraint(1, 0) - .build(); - } - - @Test(expected = MenderException.class) - public void testMinLengthConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withLengthConstraint(1, 2) - .build(); - mender.fix(",a,bbb,cc"); // No solution - } - - @Test - public void testMaxLengthEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthEstimations(3) - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,c"); - mender.fit("aaa,b,cc"); - try { - final String[] values = mender.fix("aaa,,,b,ccc"); - Assert.assertEquals("aaa", values[0]); // Could not have a length greater than 3 - Assert.assertEquals(",,b", values[1]); - Assert.assertEquals("ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMaxLengthEstimationsInvalid() { - DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthEstimations(0) - .build(); - } - - @Test - public void testMaxLengthEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthEstimation(1, 2) - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,c"); - mender.fit("aaa,b,cc"); - try { - final String[] values = mender.fix("aaa,,bb,,ccc"); - Assert.assertEquals("aaa,", values[0]); - Assert.assertEquals( "bb", values[1]); // Could not have a length greater than 2 - Assert.assertEquals(",ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMaxLengthEstimationInvalid() { - DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthEstimation(1, 0) - .build(); - } - - @Test - public void testMaxLengthConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthConstraint(1, 2) - .build(); - try { - final String[] values = mender.fix("aa,,bb,,cc"); - Assert.assertEquals("aa,", values[0]); - Assert.assertEquals( "bb", values[1]); // Must not have a length greater than 2 - Assert.assertEquals(",cc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testMaxLengthConstraintInvalid() { - DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthConstraint(1, 0) - .build(); - } - - @Test(expected = MenderException.class) - public void testMaxLengthConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withMaxLengthConstraint(2, 2) - .build(); - mender.fix(",a,b,cccccc"); // No solution - } - - @Test(expected = MenderException.class) - public void testMaxLengthConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withMinLengthConstraint(2, 3) - .withMaxLengthConstraint(2, 2) - .build(); - mender.fix(",a,b,cc"); // Conflict - } - - @Test - public void testRangeLengthEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withRangeLengthEstimations(2, 3) - .build(); - mender.fit("aaa,b,ccc"); - mender.fit("aaa,,cc"); - mender.fit("aaa,b,cc"); - try { - final String[] values = mender.fix("aaa,,,b,,c,c"); - Assert.assertEquals( "aaa", values[0]); // Could not have a length greater than 3 - Assert.assertEquals(",,b,", values[1]); - Assert.assertEquals( "c,c", values[2]); // Could not have a length greater than 3 - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthEstimationsInvalidMinLength() { - DsvMender.builder(",", 3) - .withRangeLengthEstimations(0, 1) - .build(); - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthEstimationsInvalidMaxLength() { - DsvMender.builder(",", 3) - .withRangeLengthEstimations(2, 1) - .build(); - } - - @Test - public void testRangeLengthEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withRangeLengthEstimation(1, 2, 3) - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bbb,cc"); - try { - final String[] values = mender.fix("aaa,,bbb,,ccc"); - Assert.assertEquals("aaa,", values[0]); - Assert.assertEquals( "bbb", values[1]); // Could not have a length greater than 3 - Assert.assertEquals(",ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthEstimationInvalidMinLength() { - DsvMender.builder(",", 3) - .withRangeLengthEstimation(1, 0, 1) - .build(); - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthEstimationInvalidMaxLength() { - DsvMender.builder(",", 3) - .withRangeLengthEstimation(1, 2, 1) - .build(); - } - - @Test - public void testRangeLengthConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withRangeLengthConstraint(1, 2, 3) - .build(); - try { - final String[] values = mender.fix("aa,,,,cc"); - Assert.assertEquals("aa", values[0]); - Assert.assertEquals(",,", values[1]); // Must not have a length lower than 2 - Assert.assertEquals("cc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthConstraintInvalidMinLength() { - DsvMender.builder(",", 3) - .withRangeLengthConstraint(1, 0, 1) - .build(); - } - - @Test(expected = IllegalArgumentException.class) - public void testRangeLengthConstraintInvalidMaxLength() { - DsvMender.builder(",", 3) - .withRangeLengthConstraint(1, 2, 1) - .build(); - } - - @Test(expected = MenderException.class) - public void testRangeLengthConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withRangeLengthConstraint(2, 5, 10) - .build(); - mender.fix(",a,b,ccc"); // No solution - } - - @Test(expected = MenderException.class) - public void testRangeLengthConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withRangeLengthConstraint(2, 1, 2) - .withRangeLengthConstraint(2, 2, 3) - .build(); - mender.fix(",a,b,ccc"); // Conflict - } - - @Test - public void testPatternEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternEstimations(Pattern.compile("[a-z]+")) - .build(); - mender.fit("aaa,bbb,ccc"); - mender.fit("aaa,bb,"); - mender.fit("aaa,b,ccc"); - try { - final String[] values = mender.fix("aaa,bb,,cc"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals( "bb", values[1]); - Assert.assertEquals(",cc", values[2]); // Pattern matched only two times - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testPatternEstimationsNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternEstimations(null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testPatternEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withPatternEstimation(2, Pattern.compile("c+")) - .build(); - mender.fit("aaa,bbb,ccc"); - mender.fit("aaa,b,cc"); - mender.fit("aaa,bbb,c"); - try { - final String[] values = mender.fix("aaa,bb,,ccc"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals("bb,", values[1]); - Assert.assertEquals("ccc", values[2]); // Had always matched - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testPatternEstimationNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternEstimation(2, null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testPatternConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternConstraint(0, Pattern.compile("a+")) - .withPatternConstraint(2, Pattern.compile("c+")) - .build(); - try { - final String[] values = mender.fix("aa,,,,,cc"); - Assert.assertEquals( "aa", values[0]); // Must only contains 'a' chars - Assert.assertEquals(",,,", values[1]); - Assert.assertEquals( "cc", values[2]); // Must only contains 'c' chars - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testPatternConstraintNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternConstraint(2, null) - .build(); - try { - mender.fix("a,b,c,"); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = MenderException.class) - public void testPatternConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withPatternConstraint(2, Pattern.compile("c+")) - .build(); - mender.fix(",a,b,cc,"); // No solution - } - - @Test(expected = MenderException.class) - public void testPatternConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withPatternConstraint(2, Pattern.compile("[^c]+")) - .withPatternConstraint(2, Pattern.compile("c+")) - .build(); - mender.fix(",a,b,cc"); // Conflict - } - - @Test - public void testContainsEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withContainsEstimations("bb") - .build(); - mender.fit("aa,bbb,ccc"); - mender.fit("aa,bb,c"); - mender.fit("aa,bb,ccc"); - try { - final String[] values = mender.fix(",aa,bb,cc,"); - Assert.assertEquals(",aa", values[0]); - Assert.assertEquals( "bb", values[1]); // Always contained "bb" substring - Assert.assertEquals("cc,", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testContainsEstimationsNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsEstimations(null) - .build(); - mender.fit("aa,bbb,ccc"); - } - - @Test - public void testContainsEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withContainsEstimation(0, "aa") - .build(); - mender.fit("aaa,bbb,ccc"); - mender.fit("aaa,b,cc"); - mender.fit("aaa,bbb,c"); - try { - final String[] values = mender.fix("a,aa,bb,ccc"); - Assert.assertEquals( "a,aa", values[0]); // Had always matched - Assert.assertEquals( "bb", values[1]); - Assert.assertEquals( "ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testContainsEstimationNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withContainsEstimation(0, null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testContainsConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsConstraint(0, "aa") - .withContainsConstraint(2, "cc") - .build(); - try { - final String[] values = mender.fix(",aa,,cc,"); - Assert.assertEquals(",aa", values[0]); // Must contains "aa" substring - Assert.assertEquals( "", values[1]); - Assert.assertEquals("cc,", values[2]); // Must contains "cc" substring - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testContainsConstraintNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsConstraint(0, null) - .build(); - try { - mender.fix(",aa,,cc,"); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = MenderException.class) - public void testContainsConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsConstraint(1, "z") - .build(); - mender.fix(",a,b,cc,"); // No solution - } - - @Test(expected = MenderException.class) - public void testContainsConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsConstraint(1, "z") - .withContainsNoneConstraint(1, "z") - .build(); - mender.fix(",a,b,cc"); // Conflict - } - - @Test - public void testContainsNoneConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsNoneConstraint(0, ",") - .withContainsNoneConstraint(2, ",") - .build(); - try { - final String[] values = mender.fix("aa,,,,,cc"); - Assert.assertEquals( "aa", values[0]); // Must contains "," substring - Assert.assertEquals(",,,", values[1]); - Assert.assertEquals( "cc", values[2]); // Must contains "," substring - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testContainsNoneConstraintNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withContainsNoneConstraint(0, null) - .build(); - try { - mender.fix("aa,,,,,cc"); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testStartsWithEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withStartsWithEstimations(",") - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,cc"); - mender.fit("aaa,bb,ccc"); - try { - final String[] values = mender.fix("aaa,bb,,ccc"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals("bb,", values[1]); - Assert.assertEquals("ccc", values[2]); // Had never started with ',' - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testStartsWithEstimationsNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withStartsWithEstimations(null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testStartsWithEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withStartsWithEstimation(1, "b") - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,cc"); - mender.fit("aaa,bb,ccc"); - try { - final String[] values = mender.fix("a,,,bb,ccc"); - Assert.assertEquals("a,,", values[0]); - Assert.assertEquals( "bb", values[1]); // Had always started with 'b' - Assert.assertEquals("ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testStartsWithEstimationNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withStartsWithEstimation(1, null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testStartsWithConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withStartsWithConstraint(1, "b") - .build(); - try { - final String[] values = mender.fix("a,,,bb,ccc"); - Assert.assertEquals("a,,", values[0]); - Assert.assertEquals( "bb", values[1]); // Must start with 'b' - Assert.assertEquals("ccc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testStartsWithConstraintNull() { - DsvMender.builder(",", 3) - .withStartsWithConstraint(1, null) - .build(); - } - - @Test(expected = MenderException.class) - public void testStartsWithConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withStartsWithConstraint(1, "z") - .build(); - mender.fix(",a,b,cc,"); // No solution - } - - @Test(expected = MenderException.class) - public void testStartsWithConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withStartsWithConstraint(1, "b") - .withStartsWithConstraint(1, "c") - .build(); - mender.fix(",a,b,cc"); // Conflict - } - - @Test - public void testEndsWithEstimations() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withEndsWithEstimations(",") - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,cc"); - mender.fit("aaa,bb,ccc"); - try { - final String[] values = mender.fix("aaa,bbb,,cc"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals("bbb", values[1]); // Had never ended with ',' - Assert.assertEquals(",cc", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testEndsWithEstimationsNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withEndsWithEstimations(null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testEndsWithEstimation() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withEndsWithEstimation(1, "b") - .build(); - mender.fit("aaa,bb,c"); - mender.fit("aaa,bb,cc"); - mender.fit("aaa,bb,ccc"); - try { - final String[] values = mender.fix("aaa,bb,,,c"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals( "bb", values[1]); // Had always ended with 'b' - Assert.assertEquals(",,c", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testEndsWithEstimationNull() { - final DsvMender mender = DsvMender.builder(",", 3) - .withEndsWithEstimation(1, null) - .build(); - mender.fit("aaa,bbb,ccc"); - } - - @Test - public void testEndsWithConstraint() { - final DsvMender mender = DsvMender.builder(",", 3) - .withNonEmptyConstraints() - .withEndsWithConstraint(1, "b") - .build(); - try { - final String[] values = mender.fix("aaa,bb,,,c"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals( "bb", values[1]); // Must end with 'b' - Assert.assertEquals(",,c", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testEndsWithConstraintNull() { - DsvMender.builder(",", 3) - .withEndsWithConstraint(1, null) - .build(); - } - - @Test(expected = MenderException.class) - public void testEndsWithConstraintNoSolution() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withEndsWithConstraint(1, "z") - .build(); - mender.fix(",a,b,cc,"); // No solution - } - - @Test(expected = MenderException.class) - public void testEndsWithConstraintConflict() throws MenderException { - final DsvMender mender = DsvMender.builder(",", 3) - .withEndsWithConstraint(1, "b") - .withEndsWithConstraint(1, "c") - .build(); - mender.fix(",a,b,cc"); // Conflict - } - - @Test(expected = IndexOutOfBoundsException.class) - public void testEstimationInvalid() { - DsvMender.builder(",", 3) - .withEstimation(3, String::isEmpty) - .build(); - } - - @Test(expected = IndexOutOfBoundsException.class) - public void testConstraintInvalid() { - DsvMender.builder(",", 3) - .withConstraint(3, String::isEmpty) - .build(); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/dsv/DsvEvaluatorTest.java b/src/test/java/org/mender/dsv/DsvEvaluatorTest.java deleted file mode 100644 index aa502b2..0000000 --- a/src/test/java/org/mender/dsv/DsvEvaluatorTest.java +++ /dev/null @@ -1,122 +0,0 @@ -package org.mender.dsv; - -import org.junit.Assert; -import org.junit.Test; -import org.mender.criteria.Constraint; -import org.mender.criteria.Estimation; - -/** - *

{@link DsvEvaluator} unit tests.

- */ -public class DsvEvaluatorTest { - - @Test - public void testConstraint() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addConstraint(1, Constraint.of("+"::equals)); - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "-", "-"}), 0); // Second value is not "+" - Assert.assertEquals(1.0d, evaluator.evaluate(new String[] {"-", "+", "-"}), 0); // Ok - } - - @Test - public void testConstraints() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addConstraint(0, Constraint.of("-"::equals)); - evaluator.addConstraint(1, Constraint.of("+"::equals)); - evaluator.addConstraint(2, Constraint.of(value -> 2 == value.length())); - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"+", "-", "-" }), 0); // First value is not "-" - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "-", "-" }), 0); // Second value is not "+" - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "+", "-" }), 0); // Third value is not two chars length - Assert.assertEquals(1.0d, evaluator.evaluate(new String[] {"-", "+", "--"}), 0); // Ok - } - - @Test - public void testEstimation() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addEstimation(1, Estimation.of("+"::equals)); - - // Not adjusted yet - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "-", "-"}), 0); - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "+", "-"}), 0); - - evaluator.adjustEstimations(new String[] {"-", "+", "-"}); - - // Adjusted with "+" once - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "-", "-"}), 0); - Assert.assertEquals(1.0d, evaluator.evaluate(new String[] {"-", "+", "-"}), 0); - - evaluator.adjustEstimations(new String[] {"-", "-", "-"}); - - // Adjusted with "+" once and "-" once - Assert.assertEquals(0.5d, evaluator.evaluate(new String[] {"-", "-", "-"}), 0); - Assert.assertEquals(0.5d, evaluator.evaluate(new String[] {"-", "+", "-"}), 0); - } - - @Test - public void testEstimations() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addEstimation(0, Estimation.of("-"::equals)); - evaluator.addEstimation(1, Estimation.of("+"::equals)); - evaluator.addEstimation(2, Estimation.of(value -> 2 == value.length())); - - // Not adjusted yet - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"-", "+", "--"}), 0); - - evaluator.adjustEstimations(new String[] {"-", "+", "--"}); - - Assert.assertEquals(0.0d, evaluator.evaluate(new String[] {"+", "-", "-" }), 0); // First value is not "-", second value is not "+" and third value is not two chars length - Assert.assertEquals(1.0d, evaluator.evaluate(new String[] {"-", "-", "-" }), 0); // Second value is not "+" and third value is not two chars length - Assert.assertEquals(2.0d, evaluator.evaluate(new String[] {"-", "+", "-" }), 0); // Third value is not two chars length - Assert.assertEquals(3.0d, evaluator.evaluate(new String[] {"-", "+", "--"}), 0); // Ok - - evaluator.adjustEstimations(new String[] {"+", "-", "-"}); - - // Conflicts in adjustments - Assert.assertEquals(1.5d, evaluator.evaluate(new String[] {"+", "-", "-" }), 0); - Assert.assertEquals(1.5d, evaluator.evaluate(new String[] {"-", "-", "-" }), 0); - Assert.assertEquals(1.5d, evaluator.evaluate(new String[] {"-", "+", "-" }), 0); - Assert.assertEquals(1.5d, evaluator.evaluate(new String[] {"-", "+", "--"}), 0); - } - - @Test(expected = NullPointerException.class) - public void testConstraintNull() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addConstraint(0, null); - } - - @Test(expected = IndexOutOfBoundsException.class) - public void testConstraintNegativeIndex() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addConstraint(-1, Constraint.of("-"::equals)); - } - - @Test(expected = NullPointerException.class) - public void testEstimationNull() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addEstimation(0, null); - } - - @Test(expected = IndexOutOfBoundsException.class) - public void testEstimationNegativeIndex() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.addEstimation(-1, Estimation.of("-"::equals)); - } - - @Test(expected = NullPointerException.class) - public void testAdjustNull() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.adjustEstimations(null); - } - - @Test(expected = NullPointerException.class) - public void testEvaluateNull() { - final DsvEvaluator evaluator = new DsvEvaluator(); - evaluator.evaluate(null); - } - - @Test - public void testEvaluateEmpty() { - final DsvEvaluator evaluator = new DsvEvaluator(); - Assert.assertEquals(1.0d, evaluator.evaluate(new String[] {"1", "2", "3"}), 0); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/dsv/DsvMenderTest.java b/src/test/java/org/mender/dsv/DsvMenderTest.java deleted file mode 100644 index 3871e22..0000000 --- a/src/test/java/org/mender/dsv/DsvMenderTest.java +++ /dev/null @@ -1,169 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.function.Function; - -import org.junit.Assert; -import org.junit.Test; -import org.mender.MenderException; - -/** - *

{@link DsvMender} unit tests.

- */ -public class DsvMenderTest { - - @Test - public void testSimple() { - final DsvMender mender = DsvMender.auto(",", 3); - mender.fit("aaa,bbb,ccc"); - try { - final String[] values = mender.fix("aaa,bbb,c,c"); - Assert.assertEquals("aaa", values[0]); - Assert.assertEquals("bbb", values[1]); - Assert.assertEquals("c,c", values[2]); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testNullEvaluator() { - new DsvMender(null, ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - } - - @Test(expected = NullPointerException.class) - public void testNullDelimiter() { - new DsvMender(new DsvEvaluator(), null, 2, DsvMender.DEFAULT_MAX_DEPTH); - } - - @Test(expected = IllegalArgumentException.class) - public void testInvalidNbColumns() { - new DsvMender(new DsvEvaluator(), ",", 1, DsvMender.DEFAULT_MAX_DEPTH); - } - - @Test(expected = NullPointerException.class) - public void testFitRowNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - mender.fit((String) null); - } - - @Test(expected = NullPointerException.class) - public void testFitValuesNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - mender.fit((String[]) null); - } - - @Test(expected = IllegalArgumentException.class) - public void testFitValuesInvalidNbColumns() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - mender.fit(new String[] {"1", "2", "3"}); - } - - @Test(expected = IllegalArgumentException.class) - public void testFitValuesInvalidConstraint() { - final DsvMender mender = DsvMender.builder(",", 2) - .withNonEmptyConstraints() - .build(); - mender.fit(new String[] {"1", ""}); - } - - @Test(expected = NullPointerException.class) - public void testOptimizedFixRowNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - try { - mender.optimizedFix((String) null, 1); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testOptimizedFixValuesNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - try { - mender.optimizedFix((String[]) null, 1); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testFixRowNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - try { - mender.fix((String) null); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testFixValuesNull() { - final DsvMender mender = new DsvMender(new DsvEvaluator(), ",", 2, DsvMender.DEFAULT_MAX_DEPTH); - try { - mender.fix((String[]) null); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalArgumentException.class) - public void testFitInvalidColumns() { - final DsvMender mender = DsvMender.auto(",", 3); - mender.fit("aaa,bbb,ccc,ddd"); - } - - @Test(expected = IllegalArgumentException.class) - public void testFixValidColumns() { - final DsvMender mender = DsvMender.auto(",", 4); - try { - mender.fix("aaa,bbb,ccc,ddd"); - } catch (final MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test - public void testHashCode() { - final Function standard = Arrays::hashCode; - final Function custom = array -> { - try { - final Method method = DsvMender.class.getDeclaredClasses()[0].getDeclaredMethod("customHashCode", String[].class); - method.setAccessible(true); - return (int) method.invoke(null, new Object[] {array}); - } catch (final NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { - Assert.fail(e.getMessage()); - } - return null; - }; - Assert.assertEquals(standard.apply(new String[] {"00", "00"}), standard.apply(new String[] {"00", "00"})); - Assert.assertEquals( custom.apply(new String[] {"00", "00"}) , custom.apply(new String[] {"00", "00"})); - Assert.assertEquals( standard.apply(new String[] {"0", "000"}), standard.apply(new String[] {"00", "00"})); // Collision - Assert.assertNotEquals( custom.apply(new String[] {"0", "000"}), custom.apply(new String[] {"00", "00"})); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/dsv/DsvReaderTest.java b/src/test/java/org/mender/dsv/DsvReaderTest.java deleted file mode 100644 index 4858836..0000000 --- a/src/test/java/org/mender/dsv/DsvReaderTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import java.io.BufferedReader; -import java.io.IOException; - -import org.junit.Assert; -import org.junit.Test; -import org.mender.MenderException; - -public class DsvReaderTest { - - @Test - public void testSimple() { - final DsvMender mender = DsvMender.auto(",", 3); - final String dsv = "1,2,3\n" - + "aaa,bbb,ccc\n" - + "aaa,bbb,ccc\n" - + "aa,,bbb,ccc"; - try (final DsvReader reader = new DsvReader(mender, dsv)) { - Assert.assertArrayEquals(new String[] { "1", "2", "3"}, reader.readHeader()); - Assert.assertArrayEquals(new String[] {"aaa", "bbb", "ccc"}, reader.readRow()); - Assert.assertArrayEquals(new String[] {"aaa", "bbb", "ccc"}, reader.readRow()); - Assert.assertArrayEquals(new String[] {"aa,", "bbb", "ccc"}, reader.readRow()); - Assert.assertArrayEquals(null, reader.readRow()); - } catch (final IOException | MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = IllegalStateException.class) - public void testReadHeaderLate() { - final DsvMender mender = DsvMender.auto(",", 3); - final String dsv = "1,2,3\n" - + "aaa,bbb,ccc\n" - + "aaa,bbb,ccc\n" - + "aa,,bbb,ccc"; - try (final DsvReader reader = new DsvReader(mender, dsv)) { - reader.readRow(); - reader.readHeader(); - } catch (final IOException | MenderException e) { - Assert.fail(e.getMessage()); - } - } - - @Test(expected = NullPointerException.class) - public void testNullDsvMender() { - new DsvReader(null, ""); - } - - @Test(expected = NullPointerException.class) - public void testNullBufferedReader() { - new DsvReader(DsvMender.auto(",", 3), (BufferedReader) null); - } -} \ No newline at end of file diff --git a/src/test/java/org/mender/dsv/DsvUtilsTest.java b/src/test/java/org/mender/dsv/DsvUtilsTest.java deleted file mode 100644 index 1c8a9cc..0000000 --- a/src/test/java/org/mender/dsv/DsvUtilsTest.java +++ /dev/null @@ -1,81 +0,0 @@ -/* -MIT License - -Copyright (c) 2018 Alexis Jehan - -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 org.mender.dsv; - -import org.junit.Assert; -import org.junit.Test; - -/** - *

{@link DsvUtils} unit tests.

- */ -public class DsvUtilsTest { - - @Test - public void testSimple() { - Assert.assertArrayEquals( - new String[] {"a", ":", "b", ":", "c"}, - DsvUtils.optimize(new String[] {"a", "", "", "b", ":", "c"}, ":", 5, 0) - ); - } - - @Test - public void testNbSafeColumns() { - Assert.assertArrayEquals( - new String[] {"a", "", ":", "", "b", "", ":", "", "c"}, - DsvUtils.optimize(new String[] {"a", "", "", "", "", "b", "", "", "", "", "c"}, ":", 5, 1) - ); - } - - @Test - public void testTooLargeNbSafeColumns() { - Assert.assertArrayEquals( - new String[] {"a", "", "", "b", "", "", "c"}, - DsvUtils.optimize(new String[] {"a", "", "", "b", "", "", "c"}, ":", 5, 1) - ); - } - - @Test - public void testLargeConsecutive() { - Assert.assertArrayEquals( - new String[] {"a", "", "", ":::::::::::::::", "", "", "b", "c"}, - DsvUtils.optimize(new String[] {"a", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "b", "c"}, ":", 5, 2) - ); - } - - @Test - public void testFirstColumn() { - Assert.assertArrayEquals( - new String[] {"::", "a", "b", ":", "c"}, - DsvUtils.optimize(new String[] {"", "", "", "a", "b", "", "", "c"}, ":", 5, 0) - ); - } - - @Test - public void testLastColumn() { - Assert.assertArrayEquals( - new String[] {"a", ":", "b", "c", "::"}, - DsvUtils.optimize(new String[] {"a", "", "", "b", "c", "", "", ""}, ":", 5, 0) - ); - } -} \ No newline at end of file