├── .github └── workflows │ └── maven-verify.yml ├── .gitignore ├── LICENSE ├── README.md ├── RELEASE_NOTES.md ├── checkstyle-configuration.xml ├── docs ├── CSV_FILE_SUPPORT.md ├── NULL_SUPPORT.md ├── POOLING.md ├── data_frame_no_pooling.png └── data_frame_with_pooling.png ├── pom.xml └── src ├── main ├── antlr4 │ └── io │ │ └── github │ │ └── vmzakharov │ │ └── ecdataframe │ │ └── grammar │ │ └── ModelScript.g4 └── java │ └── io │ └── github │ └── vmzakharov │ └── ecdataframe │ ├── dataframe │ ├── AggregateFunction.java │ ├── DataFrame.java │ ├── DfBooleanColumn.java │ ├── DfBooleanColumnComputed.java │ ├── DfBooleanColumnStored.java │ ├── DfCellComparator.java │ ├── DfColumn.java │ ├── DfColumnAbstract.java │ ├── DfColumnComputed.java │ ├── DfColumnSortOrder.java │ ├── DfColumnStored.java │ ├── DfCursor.java │ ├── DfDateColumn.java │ ├── DfDateColumnComputed.java │ ├── DfDateColumnStored.java │ ├── DfDateTimeColumn.java │ ├── DfDateTimeColumnComputed.java │ ├── DfDateTimeColumnStored.java │ ├── DfDecimalColumn.java │ ├── DfDecimalColumnComputed.java │ ├── DfDecimalColumnStored.java │ ├── DfDoubleColumn.java │ ├── DfDoubleColumnComputed.java │ ├── DfDoubleColumnStored.java │ ├── DfFloatColumn.java │ ├── DfFloatColumnComputed.java │ ├── DfFloatColumnStored.java │ ├── DfIndex.java │ ├── DfIndexKeeper.java │ ├── DfIntColumn.java │ ├── DfIntColumnComputed.java │ ├── DfIntColumnStored.java │ ├── DfIterate.java │ ├── DfJoin.java │ ├── DfLongColumn.java │ ├── DfLongColumnComputed.java │ ├── DfLongColumnStored.java │ ├── DfObjectColumn.java │ ├── DfObjectColumnAbstract.java │ ├── DfObjectColumnComputed.java │ ├── DfObjectColumnStored.java │ ├── DfStringColumn.java │ ├── DfStringColumnComputed.java │ ├── DfStringColumnStored.java │ ├── DfTuple.java │ ├── aggregation │ │ ├── Avg.java │ │ ├── Avg2d.java │ │ ├── Count.java │ │ ├── Max.java │ │ ├── Min.java │ │ ├── Same.java │ │ └── Sum.java │ ├── compare │ │ ├── BooleanComparisonResult.java │ │ ├── ComparisonResult.java │ │ ├── DateComparisonResult.java │ │ ├── DateTimeComparisonResult.java │ │ ├── DecimalComparisonResult.java │ │ ├── DoubleComparisonResult.java │ │ ├── LongComparisonResult.java │ │ ├── ObjectComparisonResult.java │ │ └── StringComparisonResult.java │ └── util │ │ ├── DataFrameCompare.java │ │ └── DataFramePrettyPrint.java │ ├── dataset │ ├── BooleanFormatter.java │ ├── BooleanSchemaColumn.java │ ├── CsvDataSet.java │ ├── CsvSchema.java │ ├── CsvSchemaColumn.java │ ├── DataSet.java │ ├── DataSetAbstract.java │ ├── DateSchemaColumn.java │ ├── DateTimeSchemaColumn.java │ ├── DecimalFormatter.java │ ├── DecimalSchemaColumn.java │ ├── DoubleFormatter.java │ ├── DoubleSchemaColumn.java │ ├── FloatFormatter.java │ ├── FloatSchemaColumn.java │ ├── HierarchicalDataSet.java │ ├── IntFormatter.java │ ├── IntSchemaColumn.java │ ├── LongFormatter.java │ ├── LongSchemaColumn.java │ ├── ObjectListDataSet.java │ └── StringSchemaColumn.java │ ├── dsl │ ├── AbstractScript.java │ ├── AliasExpr.java │ ├── AnonymousScript.java │ ├── ArithmeticOp.java │ ├── AssignExpr.java │ ├── BinaryExpr.java │ ├── BinaryOp.java │ ├── BooleanOp.java │ ├── ComparisonOp.java │ ├── ContainsOp.java │ ├── DecimalExpr.java │ ├── EvalContext.java │ ├── EvalContextAbstract.java │ ├── Expression.java │ ├── FunctionCallExpr.java │ ├── FunctionDescriptor.java │ ├── FunctionScript.java │ ├── IfElseExpr.java │ ├── IndexExpr.java │ ├── PredicateOp.java │ ├── ProjectionExpr.java │ ├── PropertyPathExpr.java │ ├── Script.java │ ├── SimpleEvalContext.java │ ├── StatementSequenceScript.java │ ├── UnaryExpr.java │ ├── UnaryOp.java │ ├── VarExpr.java │ ├── VectorExpr.java │ ├── function │ │ ├── BuiltInFunctions.java │ │ ├── IntrinsicFunctionDescriptor.java │ │ └── IntrinsicFunctionDescriptorBuilder.java │ ├── value │ │ ├── BooleanValue.java │ │ ├── DataFrameValue.java │ │ ├── DateTimeValue.java │ │ ├── DateValue.java │ │ ├── DecimalValue.java │ │ ├── DoubleValue.java │ │ ├── FloatValue.java │ │ ├── IntValue.java │ │ ├── LongValue.java │ │ ├── NumberValue.java │ │ ├── RealNumberValue.java │ │ ├── StringValue.java │ │ ├── Value.java │ │ ├── ValueType.java │ │ ├── VectorValue.java │ │ └── WholeNumberValue.java │ └── visitor │ │ ├── ExpressionEvaluationVisitor.java │ │ ├── ExpressionVisitor.java │ │ ├── InMemoryEvaluationVisitor.java │ │ ├── PrettyPrintVisitor.java │ │ └── TypeInferenceVisitor.java │ ├── grammar │ ├── CollectingErrorListener.java │ └── ModelScriptTreeBuilderVisitor.java │ └── util │ ├── CollectingPrinter.java │ ├── ConfigureMessages.java │ ├── ExceptionFactory.java │ ├── ExpressionParserHelper.java │ ├── FormatWithPlaceholders.java │ ├── Printer.java │ ├── PrinterFactory.java │ ├── Stopwatch.java │ ├── SysErrPrinter.java │ └── SysOutPrinter.java └── test ├── java └── io │ └── github │ └── vmzakharov │ └── ecdataframe │ ├── AggregateFunctionTest.java │ ├── DateExpressionTest.java │ ├── ExpressionFloatValueTest.java │ ├── ExpressionIntValueTest.java │ ├── ExpressionTestUtil.java │ ├── ExpressionWithNullValuesTest.java │ ├── FunctionDeclarationsTest.java │ ├── HandcraftedExpressionTreeTest.java │ ├── ImmutableVariablesTest.java │ ├── IsEmptyOperationTest.java │ ├── ParserErrorReportingTest.java │ ├── ScriptFromStringTest.java │ ├── SimpleExpressionParsingTest.java │ ├── StandaloneExpressionFromStringTest.java │ ├── TypeInferenceForBuiltInFunctionsTest.java │ ├── TypeInferenceTest.java │ ├── TypeInferenceUtil.java │ ├── VectorExpressionTest.java │ ├── dataframe │ ├── BasicDataFrameTest.java │ ├── ColumnIteratorTest.java │ ├── DataFrameAddColumnWithDataTest.java │ ├── DataFrameAggregationAvg2dTest.java │ ├── DataFrameAggregationErrorMessageTest.java │ ├── DataFrameAggregationNoGroupingTest.java │ ├── DataFrameAggregationNullsAreOkayTest.java │ ├── DataFrameAggregationNullsArePoisonousTest.java │ ├── DataFrameAggregationSameTest.java │ ├── DataFrameAggregationTest.java │ ├── DataFrameAggregationWithCustomFunctionsTest.java │ ├── DataFrameAggregationWithGroupByTest.java │ ├── DataFrameAggregationWithIndexTest.java │ ├── DataFrameBitmapTest.java │ ├── DataFrameBooleanColumnTest.java │ ├── DataFrameComputedColumnsFailureTest.java │ ├── DataFrameComputedColumnsTest.java │ ├── DataFrameComputedWithInferredTypeTest.java │ ├── DataFrameComputedWithNullsTest.java │ ├── DataFrameCopyTest.java │ ├── DataFrameDistinctTest.java │ ├── DataFrameExpressionTest.java │ ├── DataFrameFilterTest.java │ ├── DataFrameIndexTest.java │ ├── DataFrameIterateTest.java │ ├── DataFrameJoinTest.java │ ├── DataFrameLookupJoinTest.java │ ├── DataFramePivotColumnSortTest.java │ ├── DataFramePivotTest.java │ ├── DataFramePoolingTest.java │ ├── DataFrameSortDirectionTest.java │ ├── DataFrameSortTest.java │ ├── DataFrameToStringTest.java │ ├── DataFrameUnionTest.java │ ├── DataFrameUtil.java │ ├── DataFrameWithDecimalColumnTest.java │ ├── DfColumnCompareTest.java │ ├── IntersectionAndDifferencesTest.java │ ├── TupleCompareTest.java │ ├── bench │ │ └── DataFrameParallelTest.java │ └── util │ │ ├── DataFrameCompareTest.java │ │ └── DataFramePrettyPrintTest.java │ ├── dataset │ ├── CsvCodeScratchpad.java │ ├── DataFrameLoadBooleanTest.java │ ├── DataFrameLoadFromZippedTest.java │ ├── DataFrameLoadTest.java │ ├── DataFrameWriteBooleanTest.java │ ├── DataFrameWriteTest.java │ ├── FormattedColumnsTest.java │ ├── SeparatedTextSplittingTest.java │ └── StringBasedCsvDataSet.java │ ├── dsl │ ├── DecimalExpressionTest.java │ ├── ObjectValueCompareOpTest.java │ ├── ObjectValueContainsOpTest.java │ ├── PrettyPrintingTest.java │ ├── ValueCompareTest.java │ ├── function │ │ ├── BuiltInFormatterTest.java │ │ ├── BuiltInFunctionTest.java │ │ └── RuntimeAddedFunctionTest.java │ ├── projection │ │ ├── Donut.java │ │ ├── HierarchicalDataSetProjectionTest.java │ │ ├── Person.java │ │ └── ProjectionExtraTypesTest.java │ └── value │ │ └── ValueTest.java │ └── util │ ├── ExceptionFactoryTest.java │ └── FormatWithPlaceholderTest.java └── resources ├── Messages.properties └── employees.csv /.github/workflows/maven-verify.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven, and cache/restore any dependencies to improve the workflow execution time 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: Maven Verify Phase 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Set up JDK 17 16 | uses: actions/setup-java@v4 17 | with: 18 | java-version: '17' 19 | distribution: 'temurin' 20 | cache: maven 21 | - name: Run Maven Verify Phase 22 | run: mvn --batch-mode verify --file pom.xml 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | #intelliJ 26 | .idea 27 | *.iml 28 | *.ipr 29 | 30 | #maven 31 | target/ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Vladimir Zakharov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/POOLING.md: -------------------------------------------------------------------------------- 1 | # Pooling 2 | 3 | ## Introduction 4 | 5 | Data Frame instances support (optional) **pooling** of object values. Pooling means that as an object is added to the data frame, it is first looked up in the pool and if an equal object is found, that reference is used. If an equal object is not found, the new object is added to the pool. In other words, with pooling for objects that are equal the memory needs to be allocated for only one instance. 6 | 7 | A pool is scoped for a column. 8 | 9 | For columns with a lot of repeating values pooling can result in substantial memory savings and, in certain scenarios, in performance improvements. 10 | 11 | ### Example 12 | Let's say there is a data frame storing customer orders. There are three columns: customer name (string), order number (integer), order date (date). 13 | 14 | There are 10 rows in the data frame. 15 | 16 | Without pooling there are 10 instances of string objects and 10 instances of date objects. 17 | 18 | ![](data_frame_no_pooling.png) 19 | 20 | With pooling, there are 4 instances of strings and 3 instances of dates. By Grabthar's Hammer... what a savings. 21 | 22 | ![](data_frame_with_pooling.png) 23 | 24 | ## Managing Pooling 25 | 26 | ### Enabling and Disabling 27 | 28 | Pooling can be enabled and disabled at the data frame or at the individual column level. 29 | 30 | To enabling pooling at the data frame level, call `enablePooling()` on a data frame instance, for example 31 | 32 | ```java 33 | DataFrame df = new DataFrame("Data Frame"); 34 | df.enablePooling(); 35 | // ...populate data frame... 36 | ``` 37 | 38 | This method will enable pooling on all data frame columns (specifically, object columns, as there is no pooling of primitive values, so enabling pooling on primitive columns does nothing.) 39 | 40 | To enable pooling for a specific column call `enablePooling()` on this column, for example 41 | ```java 42 | DataFrame df = new DataFrame("Data Frame"); 43 | df.getColumnNamed("name").enablePooling(); 44 | // ...populate data frame... 45 | ``` 46 | 47 | Calling `enablePooling()` on a data frame (or a column) with data will cause the necessary pools to be rebuilt, and the data repopulated, potentially resulting in memory compression at the cost of this extra work. 48 | 49 | To disable pooling call `disablePooling()` on either a data frame instance or a column instance, depending on the desired scope. 50 | 51 | Note that calling `seal()` method (indicating that the dataframe construction is complete) will in turn call `disablePooling()`. 52 | 53 | ### Data Frame Transformation and Pooling 54 | 55 | By default, the data frames created as a result of extracting data from an existing data frame (using methods such as `select`, `reject`, `partition`) will retain the pooling setting of the source data frame. 56 | 57 | Note that if the source data frame was created with pooling enabled, regardless of its current pooling status, the derived data frames will benefit from that. That is, the total number of value instances will not increase as no new instances will be created. So you only need pooling on derived data frames if you are planning to add data to them. 58 | 59 | If the source data frame has pooling enabled, and you do not need pooling for a derived data frame as you are not planning to add any data to it, call `disablePooling` on it to avoid the overhead of having an active pool. 60 | -------------------------------------------------------------------------------- /docs/data_frame_no_pooling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmzakharov/dataframe-ec/8167010ceab19aff28a62a477d61dce9217fd4f2/docs/data_frame_no_pooling.png -------------------------------------------------------------------------------- /docs/data_frame_with_pooling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vmzakharov/dataframe-ec/8167010ceab19aff28a62a477d61dce9217fd4f2/docs/data_frame_with_pooling.png -------------------------------------------------------------------------------- /src/main/antlr4/io/github/vmzakharov/ecdataframe/grammar/ModelScript.g4: -------------------------------------------------------------------------------- 1 | grammar ModelScript; 2 | 3 | script : 4 | functionDeclarationExpr* 5 | statementSequence 6 | projectionStatement? 7 | ; 8 | 9 | functionDeclarationExpr : 10 | 'function' ID ('(' idList ')' | '(' ')')? 11 | '{' 12 | statementSequence 13 | '}' 14 | ; 15 | 16 | statementSequence: statement*; 17 | 18 | statement : 19 | ID '=' expr #assignExpr 20 | | IF expr 21 | THEN ifBody=statementSequence 22 | (ELSE elseBody=statementSequence)? 23 | ENDIF #conditionExpr 24 | | expr #freeExp 25 | ; 26 | 27 | projectionStatement : 28 | 'project' '{' exprList '}' ('where' expr)? 29 | ; 30 | 31 | expr : 32 | ID ( '.' ID)+? #propertyPathExpr 33 | | ID ':' expr #aliasExpr 34 | | ID ('(' exprList ')' | '(' ')') #functionCallExpr 35 | | '(' expr ')' #parenExpr 36 | | '-' expr #unaryMinusExpr 37 | | expr IS_NOT_EMPTY #isNotEmptyExpr 38 | | expr IS_EMPTY #isEmptyExpr 39 | | expr IS_NOT_NULL #isNotNullExpr 40 | | expr IS_NULL #isNullExpr 41 | | NOT expr #notExpr 42 | | expr '[' expr ']' #indexVectorExpr 43 | | expr op=(MUL | DIV) expr #mulDivExpr 44 | | expr op=(ADD | SUB) expr #addSubExpr 45 | | expr op=(GT | GTE | LT | LTE | EQ | NE) expr #compareExpr 46 | | expr (NOT_IN | IN) expr #inExpr 47 | | expr op=AND expr #andExpr 48 | | expr op=(OR | XOR) expr #orExpr 49 | | vectorExpr #standaloneVectorExpr 50 | | INT #intLiteralExpr 51 | | DOUBLE #doubleLiteralExpr 52 | | STRING #stringLiteralExpr 53 | | ID #varExpr 54 | | condExpr=expr '?' ifExpr=expr ':' elseExpr=expr #ternaryExpr 55 | ; 56 | 57 | vectorExpr : '(' exprList ')' | '(' ')'; 58 | 59 | exprList : expr ( ',' expr )*; 60 | 61 | idList : ID ( ',' ID) *; 62 | 63 | MUL : '*' ; 64 | DIV : '/' ; 65 | ADD : '+' ; 66 | SUB : '-' ; 67 | 68 | GT : '>' ; 69 | GTE : '>=' ; 70 | LT : '<' ; 71 | LTE : '<=' ; 72 | EQ : '=='; 73 | NE : '!='; 74 | 75 | NOT_IN : 'not in' | 'NOT IN' ; 76 | IN : 'in' | 'IN' ; 77 | IS_EMPTY : 'is empty' | 'IS EMPTY' ; 78 | IS_NOT_EMPTY : 'is not empty' | 'IS NOT EMPTY' ; 79 | IS_NULL : 'is null' | 'IS NULL' ; 80 | IS_NOT_NULL : 'is not null' | 'IS NOT NULL' ; 81 | 82 | AND : 'and' | 'AND'; 83 | OR : 'or' | 'OR'; 84 | XOR : 'xor' | 'XOR'; 85 | 86 | NOT : 'not' | 'NOT' | '!'; 87 | 88 | IF : 'if' | 'IF'; 89 | THEN : 'then' | 'THEN'; 90 | ELSE : 'else' | 'ELSE'; 91 | ENDIF : 'endif' | 'ENDIF'; 92 | 93 | ID : '${' .*? '}' | LETTER (LETTER|DIGIT)* ; 94 | INT : DIGIT+; 95 | DOUBLE : ('.' DIGIT+ | DIGIT+ ('.' DIGIT*)? ) ; 96 | LINE_COMMENT : '//' ~[\r\n]* -> skip; 97 | WS : [ \t]+ -> skip; 98 | NL : [\n\r] -> skip; 99 | STRING : '"' ( ESC | . )*? '"' | '\'' ( ESC | . )*? '\''; 100 | 101 | fragment 102 | ESC : '\\' [btnr"\\]; 103 | LETTER : [a-zA-Z\u0080-\u00FF_]; 104 | DIGIT : [0-9]; -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfBooleanColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.BooleanValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 7 | 8 | public class DfBooleanColumnComputed 9 | extends DfBooleanColumn 10 | implements DfColumnComputed 11 | { 12 | private final String expressionAsString; 13 | private final Expression expression; 14 | 15 | public DfBooleanColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 16 | { 17 | super(newDataFrame, newName); 18 | this.expressionAsString = newExpressionAsString; 19 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 20 | } 21 | 22 | @Override 23 | public boolean getBoolean(int rowIndex) 24 | { 25 | Value result = this.getValue(rowIndex); 26 | 27 | if (result.isVoid()) 28 | { 29 | throw new NullPointerException("Null value at " + this.getName() + "[" + rowIndex + "]"); 30 | } 31 | 32 | return ((BooleanValue) result).isTrue(); 33 | } 34 | 35 | @Override 36 | public Object getObject(int rowIndex) 37 | { 38 | Value result = this.getValue(rowIndex); 39 | 40 | return result.isVoid() ? null : ((BooleanValue) result).isTrue(); 41 | } 42 | 43 | @Override 44 | public boolean isNull(int rowIndex) 45 | { 46 | return this.getObject(rowIndex) == null; 47 | } 48 | 49 | @Override 50 | protected void addAllItemsFrom(DfBooleanColumn booleanColumn) 51 | { 52 | this.throwUnmodifiableColumnException(); 53 | } 54 | 55 | @Override 56 | public int getSize() 57 | { 58 | return this.getDataFrame().rowCount(); 59 | } 60 | 61 | @Override 62 | public String getExpressionAsString() 63 | { 64 | return this.expressionAsString; 65 | } 66 | 67 | @Override 68 | public Expression getExpression() 69 | { 70 | return this.expression; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfCellComparator.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.ComparisonResult; 4 | 5 | public interface DfCellComparator 6 | { 7 | ComparisonResult compare(int thisRowIndex, int otherRowIndex); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | 6 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 7 | 8 | public interface DfColumn 9 | { 10 | String getName(); 11 | 12 | String getValueAsString(int rowIndex); 13 | 14 | default String getValueAsStringLiteral(int rowIndex) 15 | { 16 | return this.isNull(rowIndex) ? "" : this.getValueAsString(rowIndex); 17 | } 18 | 19 | void addObject(Object newObject); 20 | 21 | void addValue(Value value); 22 | 23 | boolean isNull(int rowIndex); 24 | 25 | Object getObject(int rowIndex); 26 | 27 | DataFrame getDataFrame(); 28 | 29 | Value getValue(int rowIndex); 30 | 31 | ValueType getType(); 32 | 33 | boolean isStored(); 34 | 35 | default boolean isComputed() 36 | { 37 | return !this.isStored(); 38 | } 39 | 40 | int getSize(); 41 | 42 | void setObject(int rowIndex, Object anObject); 43 | 44 | void addEmptyValue(); 45 | 46 | default Object aggregate(AggregateFunction aggregateFunction) 47 | { 48 | if (!aggregateFunction.supportsSourceType(this.getType())) 49 | { 50 | throw aggregateFunction.notApplicable(this); 51 | } 52 | 53 | if (this.getSize() == 0) 54 | { 55 | return aggregateFunction.valueForEmptyColumn(this); 56 | } 57 | 58 | try 59 | { 60 | return aggregateFunction.applyToColumn(this); 61 | } 62 | catch (NullPointerException npe) 63 | { 64 | // npe can be thrown if there is a null value stored in a column of primitive type, this can happen when 65 | // converting column values to a list. 66 | return null; 67 | } 68 | } 69 | 70 | void applyAggregator(int targetRowIndex, DfColumn sourceColumn, int sourceRowIndex, AggregateFunction aggregateFunction); 71 | 72 | DfColumn cloneSchemaAndAttachTo(DataFrame attachTo); 73 | 74 | DfColumn cloneSchemaAndAttachTo(DataFrame attachTo, String newName); 75 | 76 | void addRowToColumn(int rowIndex, DfColumn target); 77 | 78 | default void enablePooling() 79 | { 80 | // nothing 81 | } 82 | 83 | default void disablePooling() 84 | { 85 | // nothing 86 | } 87 | 88 | DfColumn mergeWithInto(DfColumn other, DataFrame target); 89 | 90 | DfColumn copyTo(DataFrame target); 91 | 92 | default DfCellComparator columnComparator(DfColumn otherColumn) 93 | { 94 | throw exceptionByKey("DF_NO_COL_COMPARATOR") 95 | .with("columnName", this.getName()) 96 | .with("columnType", this.getType()) 97 | .getUnsupported(); 98 | } 99 | 100 | default int dataFrameRowIndex(int virtualRowIndex) 101 | { 102 | return this.getDataFrame().rowIndexMap(virtualRowIndex); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfColumnAbstract.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | 5 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 6 | 7 | public abstract class DfColumnAbstract 8 | implements DfColumn 9 | { 10 | final private String name; 11 | 12 | private DataFrame dataFrame; 13 | 14 | public DfColumnAbstract(DataFrame newDataFrame, String newName) 15 | { 16 | this.dataFrame = newDataFrame; 17 | this.name = newName; 18 | } 19 | 20 | @Override 21 | public String getName() 22 | { 23 | return this.name; 24 | } 25 | 26 | @Override 27 | public DataFrame getDataFrame() 28 | { 29 | return this.dataFrame; 30 | } 31 | 32 | public void setDataFrame(DataFrame newDataFrame) 33 | { 34 | if (this.dataFrame != null) 35 | { 36 | exceptionByKey("DF_COL_ALREADY_LINKED").with("columnName", this.getName()).fire(); 37 | } 38 | 39 | this.dataFrame = newDataFrame; 40 | } 41 | 42 | @Override 43 | public DfColumn cloneSchemaAndAttachTo(DataFrame attachTo) 44 | { 45 | return this.cloneSchemaAndAttachTo(attachTo, this.getName()); 46 | } 47 | 48 | @Override 49 | public DfColumn cloneSchemaAndAttachTo(DataFrame attachTo, String newName) 50 | { 51 | return this.isStored() 52 | ? 53 | attachTo.newColumn(newName, this.getType()) 54 | : 55 | attachTo.newColumn(newName, this.getType(), ((DfColumnComputed) this).getExpressionAsString()); 56 | } 57 | 58 | protected DfColumn validateAndCreateTargetColumn(DfColumn other, DataFrame target) 59 | { 60 | if (!this.getType().equals(other.getType())) 61 | { 62 | exceptionByKey("DF_MERGE_COL_DIFF_TYPES") 63 | .with("firstColumnName", this.getName()) 64 | .with("firstColumnType", this.getType()) 65 | .with("secondColumnName", other.getName()) 66 | .with("secondColumnType", other.getType()) 67 | .fire(); 68 | } 69 | 70 | DfColumnStored newColumn = target.newColumn(this.getName(), this.getType()); 71 | 72 | newColumn.ensureInitialCapacity(this.getSize() + other.getSize()); 73 | 74 | return newColumn; 75 | } 76 | 77 | protected DfColumn copyColumnSchemaAndEnsureCapacity(DataFrame target) 78 | { 79 | DfColumnStored newColumn = target.newColumn(this.getName(), this.getType()); 80 | 81 | newColumn.ensureInitialCapacity(this.getSize()); 82 | 83 | return newColumn; 84 | } 85 | 86 | protected void throwAddingIncompatibleValueException(Value value) 87 | { 88 | exceptionByKey("DF_BAD_VAL_ADD_TO_COL") 89 | .with("valueType", value.getType()) 90 | .with("columnName", this.getName()) 91 | .with("columnType", this.getType()) 92 | .with("value", value.asStringLiteral()) 93 | .fire(); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 7 | 8 | public interface DfColumnComputed 9 | extends DfColumn 10 | { 11 | @Override 12 | default boolean isStored() 13 | { 14 | return false; 15 | } 16 | 17 | String getExpressionAsString(); 18 | 19 | Expression getExpression(); 20 | 21 | @Override 22 | default void setObject(int rowIndex, Object anObject) 23 | { 24 | exceptionByKey("DF_SET_VAL_ON_COMP_COL").with("columnName", this.getName()).fire(); 25 | } 26 | 27 | @Override 28 | default Value getValue(int rowIndex) 29 | { 30 | this.getDataFrame().setEvalContextRowIndex(rowIndex); 31 | 32 | return this.getExpression().evaluate(this.getDataFrame().getEvalVisitor()); 33 | } 34 | 35 | @Override 36 | default void addEmptyValue() 37 | { 38 | // no-op 39 | } 40 | 41 | @Override 42 | default void addValue(Value value) 43 | { 44 | this.throwUnmodifiableColumnException(); 45 | } 46 | 47 | /** 48 | * a placeholder method for computed columns defined so that {@code addRow()} could be called on a data frame with 49 | * computed columns. If any value other than {@code null} is passed the method throws an exception 50 | * @param newObject an object to add to the column, expected {@code null} for computed columns 51 | */ 52 | @Override 53 | default void addObject(Object newObject) 54 | { 55 | if (newObject != null) 56 | { 57 | this.throwUnmodifiableColumnException(); 58 | } 59 | } 60 | 61 | @Override 62 | default void applyAggregator(int targetRowIndex, DfColumn sourceColumn, int sourceRowIndex, AggregateFunction aggregateFunction) 63 | { 64 | exceptionByKey("DF_AGG_VAL_TO_COMP_COL").with("columnNane", this.getName()).fire(); 65 | } 66 | 67 | default void throwUnmodifiableColumnException() 68 | { 69 | throw exceptionByKey("DF_CALC_COL_MODIFICATION").with("columnNane", this.getName()).getUnsupported(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfColumnSortOrder.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | public enum DfColumnSortOrder 4 | { 5 | ASC, DESC; 6 | 7 | public int order(int comparisonResult) 8 | { 9 | return this == DESC ? -comparisonResult : comparisonResult; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfColumnStored.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | public interface DfColumnStored 4 | extends DfColumn 5 | { 6 | @Override 7 | default boolean isStored() 8 | { 9 | return true; 10 | } 11 | 12 | void ensureInitialCapacity(int newCapacity); 13 | 14 | @Override 15 | default void applyAggregator(int targetRowIndex, DfColumn sourceColumn, int sourceRowIndex, AggregateFunction aggregator) 16 | { 17 | if (aggregator.nullsArePoisonous()) 18 | { 19 | if (this.isNull(targetRowIndex)) 20 | { 21 | return; 22 | } 23 | 24 | if (sourceColumn.isNull(sourceRowIndex)) 25 | { 26 | this.setObject(targetRowIndex, null); 27 | return; 28 | } 29 | } 30 | 31 | this.aggregateValueInto(targetRowIndex, sourceColumn, sourceRowIndex, aggregator); 32 | } 33 | 34 | /** 35 | * protected - do not call, to be implemented by the subtypes 36 | * calls the provided aggregator on a value of sourceColumn at sourceRowIndex with the 37 | * combined with the current aggregated value at rowIndex of this column 38 | * 39 | * @param rowIndex - the row index in this column at which to store the new aggregated value 40 | * @param sourceColumn - the column from which to extract the new value to be aggregated 41 | * @param sourceRowIndex - the row index of the new value to be aggregated 42 | * @param aggregator - the aggregate function to be applied to the existing aggregate value in this column and 43 | * the new value extracted from the sourceColumn 44 | */ 45 | void aggregateValueInto(int rowIndex, DfColumn sourceColumn, int sourceRowIndex, AggregateFunction aggregator); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfCursor.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import java.math.BigDecimal; 4 | import java.time.LocalDate; 5 | import java.time.LocalDateTime; 6 | 7 | public class DfCursor 8 | { 9 | private final DataFrame dataFrame; 10 | private int rowIndex; 11 | 12 | public DfCursor(DataFrame newDataFrame) 13 | { 14 | this.dataFrame = newDataFrame; 15 | } 16 | 17 | public String getString(String columnName) 18 | { 19 | return this.dataFrame.getString(columnName, this.rowIndex); 20 | } 21 | 22 | public long getLong(String columnName) 23 | { 24 | return this.dataFrame.getLong(columnName, this.rowIndex); 25 | } 26 | 27 | public long getInt(String columnName) 28 | { 29 | return this.dataFrame.getInt(columnName, this.rowIndex); 30 | } 31 | 32 | public double getDouble(String columnName) 33 | { 34 | return this.dataFrame.getDouble(columnName, this.rowIndex); 35 | } 36 | 37 | public float getFloat(String columnName) 38 | { 39 | return this.dataFrame.getFloat(columnName, this.rowIndex); 40 | } 41 | 42 | public boolean getBoolean(String columnName) 43 | { 44 | return this.dataFrame.getBoolean(columnName, this.rowIndex); 45 | } 46 | 47 | public BigDecimal getDecimal(String columnName) 48 | { 49 | return this.dataFrame.getDecimal(columnName, this.rowIndex); 50 | } 51 | 52 | public LocalDate getDate(String columnName) 53 | { 54 | return this.dataFrame.getDate(columnName, this.rowIndex); 55 | } 56 | 57 | public LocalDateTime getDateTime(String columnName) 58 | { 59 | return this.dataFrame.getDateTime(columnName, this.rowIndex); 60 | } 61 | 62 | public Object getObject(String columnName) 63 | { 64 | return this.dataFrame.getObject(columnName, this.rowIndex); 65 | } 66 | 67 | public int rowIndex() 68 | { 69 | return this.rowIndex; 70 | } 71 | 72 | public DfCursor rowIndex(int newRowIndex) 73 | { 74 | this.rowIndex = newRowIndex; 75 | return this; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.DateComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.DateValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | import java.time.LocalDate; 9 | import java.time.format.DateTimeFormatter; 10 | 11 | public interface DfDateColumn 12 | extends DfObjectColumn 13 | { 14 | DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE; 15 | 16 | @Override 17 | default String getValueAsString(int rowIndex) 18 | { 19 | LocalDate value = this.getTypedObject(rowIndex); 20 | return value == null ? "" : this.FORMATTER.format(value); 21 | } 22 | 23 | @Override 24 | default ValueType getType() 25 | { 26 | return ValueType.DATE; 27 | } 28 | 29 | @Override 30 | default Value objectToValue(LocalDate anObject) 31 | { 32 | return new DateValue(anObject); 33 | } 34 | 35 | @Override 36 | default void addRowToColumn(int rowIndex, DfColumn target) 37 | { 38 | ((DfDateColumnStored) target).addMyType(this.getTypedObject(rowIndex)); 39 | } 40 | 41 | @Override 42 | default DfCellComparator columnComparator(DfColumn otherColumn) 43 | { 44 | DfDateColumn otherStringColumn = (DfDateColumn) otherColumn; 45 | 46 | return (thisRowIndex, otherRowIndex) -> new DateComparisonResult( 47 | this.getTypedObject(this.dataFrameRowIndex(thisRowIndex)), 48 | otherStringColumn.getTypedObject(otherStringColumn.dataFrameRowIndex(otherRowIndex))); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DateValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import java.time.LocalDate; 7 | 8 | public class DfDateColumnComputed 9 | extends DfObjectColumnComputed 10 | implements DfDateColumn 11 | { 12 | public DfDateColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 13 | { 14 | super(newDataFrame, newName, newExpressionAsString); 15 | } 16 | 17 | @Override 18 | public LocalDate getTypedObject(int rowIndex) 19 | { 20 | Value result = this.getValue(rowIndex); 21 | 22 | return result.isVoid() ? null : ((DateValue) result).dateValue(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateColumnStored.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DateValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import org.eclipse.collections.api.list.ListIterable; 6 | 7 | import java.time.LocalDate; 8 | 9 | public class DfDateColumnStored 10 | extends DfObjectColumnStored 11 | implements DfDateColumn 12 | { 13 | public DfDateColumnStored(DataFrame owner, String newName) 14 | { 15 | super(owner, newName); 16 | } 17 | 18 | public DfDateColumnStored(DataFrame owner, String newName, ListIterable newValues) 19 | { 20 | super(owner, newName, newValues); 21 | } 22 | 23 | @Override 24 | public void addObject(Object newObject) 25 | { 26 | this.addMyType((LocalDate) newObject); 27 | } 28 | 29 | @Override 30 | public void addValue(Value value) 31 | { 32 | if (value.isVoid()) 33 | { 34 | this.addObject(null); 35 | } 36 | else if (value.isDate()) 37 | { 38 | this.addMyType(((DateValue) value).dateValue()); 39 | } 40 | else 41 | { 42 | this.throwAddingIncompatibleValueException(value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateTimeColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.DateTimeComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.DateTimeValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | import java.time.LocalDateTime; 9 | import java.time.format.DateTimeFormatter; 10 | 11 | public interface DfDateTimeColumn 12 | extends DfObjectColumn 13 | { 14 | DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME; 15 | 16 | @Override 17 | default String getValueAsString(int rowIndex) 18 | { 19 | LocalDateTime value = this.getTypedObject(rowIndex); 20 | return value == null ? "" : this.FORMATTER.format(value); 21 | } 22 | 23 | @Override 24 | default ValueType getType() 25 | { 26 | return ValueType.DATE_TIME; 27 | } 28 | 29 | @Override 30 | default Value objectToValue(LocalDateTime anObject) 31 | { 32 | return new DateTimeValue(anObject); 33 | } 34 | 35 | @Override 36 | default void addRowToColumn(int rowIndex, DfColumn target) 37 | { 38 | ((DfDateTimeColumnStored) target).addMyType(this.getTypedObject(rowIndex)); 39 | } 40 | 41 | @Override 42 | default DfCellComparator columnComparator(DfColumn otherColumn) 43 | { 44 | DfDateTimeColumn otherStringColumn = (DfDateTimeColumn) otherColumn; 45 | 46 | return (thisRowIndex, otherRowIndex) -> new DateTimeComparisonResult( 47 | this.getTypedObject(this.dataFrameRowIndex(thisRowIndex)), 48 | otherStringColumn.getTypedObject(otherStringColumn.dataFrameRowIndex(otherRowIndex))); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateTimeColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DateTimeValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import java.time.LocalDateTime; 7 | 8 | public class DfDateTimeColumnComputed 9 | extends DfObjectColumnComputed 10 | implements DfDateTimeColumn 11 | { 12 | public DfDateTimeColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 13 | { 14 | super(newDataFrame, newName, newExpressionAsString); 15 | } 16 | 17 | @Override 18 | public LocalDateTime getTypedObject(int rowIndex) 19 | { 20 | Value result = this.getValue(rowIndex); 21 | 22 | return result.isVoid() ? null : ((DateTimeValue) result).dateTimeValue(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDateTimeColumnStored.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DateTimeValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import org.eclipse.collections.api.list.ListIterable; 6 | 7 | import java.time.LocalDateTime; 8 | 9 | public class DfDateTimeColumnStored 10 | extends DfObjectColumnStored 11 | implements DfDateTimeColumn 12 | { 13 | public DfDateTimeColumnStored(DataFrame owner, String newName) 14 | { 15 | super(owner, newName); 16 | } 17 | 18 | public DfDateTimeColumnStored(DataFrame owner, String newName, ListIterable newValues) 19 | { 20 | super(owner, newName, newValues); 21 | } 22 | 23 | @Override 24 | public void addObject(Object newObject) 25 | { 26 | this.addMyType((LocalDateTime) newObject); 27 | } 28 | 29 | @Override 30 | public void addValue(Value value) 31 | { 32 | if (value.isVoid()) 33 | { 34 | this.addObject(null); 35 | } 36 | else if (value.isDateTime()) 37 | { 38 | this.addMyType(((DateTimeValue) value).dateTimeValue()); 39 | } 40 | else 41 | { 42 | this.throwAddingIncompatibleValueException(value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDecimalColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.DecimalComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.DecimalValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | import java.math.BigDecimal; 9 | 10 | public interface DfDecimalColumn 11 | extends DfObjectColumn 12 | { 13 | @Override 14 | default String getValueAsString(int rowIndex) 15 | { 16 | BigDecimal value = this.getTypedObject(rowIndex); 17 | return value == null ? "" : value.toString(); 18 | } 19 | 20 | @Override 21 | default ValueType getType() 22 | { 23 | return ValueType.DECIMAL; 24 | } 25 | 26 | @Override 27 | default Value objectToValue(BigDecimal anObject) 28 | { 29 | return new DecimalValue(anObject); 30 | } 31 | 32 | @Override 33 | default void addRowToColumn(int rowIndex, DfColumn target) 34 | { 35 | target.addObject(this.getTypedObject(rowIndex)); 36 | } 37 | 38 | @Override 39 | default DfCellComparator columnComparator(DfColumn otherColumn) 40 | { 41 | DfDecimalColumn otherStringColumn = (DfDecimalColumn) otherColumn; 42 | 43 | return (thisRowIndex, otherRowIndex) -> new DecimalComparisonResult( 44 | this.getTypedObject(this.dataFrameRowIndex(thisRowIndex)), 45 | otherStringColumn.getTypedObject(otherStringColumn.dataFrameRowIndex(otherRowIndex))); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDecimalColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DecimalValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import java.math.BigDecimal; 7 | 8 | public class DfDecimalColumnComputed 9 | extends DfObjectColumnComputed 10 | implements DfDecimalColumn 11 | { 12 | public DfDecimalColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 13 | { 14 | super(newDataFrame, newName, newExpressionAsString); 15 | } 16 | 17 | @Override 18 | public BigDecimal getTypedObject(int rowIndex) 19 | { 20 | Value result = this.getValue(rowIndex); 21 | 22 | return result.isVoid() ? null : ((DecimalValue) result).decimalValue(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDecimalColumnStored.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DecimalValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import org.eclipse.collections.api.list.ListIterable; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public class DfDecimalColumnStored 10 | extends DfObjectColumnStored 11 | implements DfDecimalColumn 12 | { 13 | public DfDecimalColumnStored(DataFrame owner, String newName) 14 | { 15 | super(owner, newName); 16 | } 17 | 18 | public DfDecimalColumnStored(DataFrame owner, String newName, ListIterable newValues) 19 | { 20 | super(owner, newName, newValues); 21 | } 22 | 23 | @Override 24 | public void addObject(Object newObject) 25 | { 26 | this.addMyType((BigDecimal) newObject); 27 | } 28 | 29 | @Override 30 | public void addValue(Value value) 31 | { 32 | if (value.isVoid()) 33 | { 34 | this.addObject(null); 35 | } 36 | else if (value.isDecimal()) 37 | { 38 | this.addMyType(((DecimalValue) value).decimalValue()); 39 | } 40 | else 41 | { 42 | this.throwAddingIncompatibleValueException(value); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfDoubleColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.DoubleValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 7 | 8 | public class DfDoubleColumnComputed 9 | extends DfDoubleColumn 10 | implements DfColumnComputed 11 | { 12 | private final String expressionAsString; 13 | private final Expression expression; 14 | 15 | public DfDoubleColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 16 | { 17 | super(newDataFrame, newName); 18 | this.expressionAsString = newExpressionAsString; 19 | 20 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 21 | } 22 | 23 | @Override 24 | public Object getObject(int rowIndex) 25 | { 26 | Value result = this.getValue(rowIndex); 27 | 28 | return result.isVoid() ? null : ((DoubleValue) result).doubleValue(); 29 | } 30 | 31 | @Override 32 | public double getDouble(int rowIndex) 33 | { 34 | Value result = this.getValue(rowIndex); 35 | 36 | if (result.isVoid()) 37 | { 38 | throw new NullPointerException("Null value at " + this.getName() + "[" + rowIndex + "]"); 39 | } 40 | 41 | return ((DoubleValue) result).doubleValue(); 42 | } 43 | 44 | @Override 45 | protected void addAllItemsFrom(DfDoubleColumn doubleColumn) 46 | { 47 | this.throwUnmodifiableColumnException(); 48 | } 49 | 50 | @Override 51 | public int getSize() 52 | { 53 | return this.getDataFrame().rowCount(); 54 | } 55 | 56 | @Override 57 | public String getExpressionAsString() 58 | { 59 | return this.expressionAsString; 60 | } 61 | 62 | @Override 63 | public Expression getExpression() 64 | { 65 | return this.expression; 66 | } 67 | 68 | @Override 69 | public boolean isNull(int rowIndex) 70 | { 71 | return this.getObject(rowIndex) == null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfFloatColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.DoubleComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | import org.eclipse.collections.api.FloatIterable; 6 | import org.eclipse.collections.api.list.primitive.ImmutableFloatList; 7 | import org.eclipse.collections.impl.factory.primitive.FloatLists; 8 | import org.eclipse.collections.impl.list.primitive.IntInterval; 9 | 10 | abstract public class DfFloatColumn 11 | extends DfColumnAbstract 12 | { 13 | public DfFloatColumn(DataFrame newDataFrame, String newName) 14 | { 15 | super(newDataFrame, newName); 16 | } 17 | 18 | abstract public float getFloat(int rowIndex); 19 | 20 | @Override 21 | public String getValueAsString(int rowIndex) 22 | { 23 | return Float.toString(this.getFloat(rowIndex)); 24 | } 25 | 26 | public ImmutableFloatList toFloatList() 27 | { 28 | return this.asFloatIterable().toList().toImmutable(); 29 | } 30 | 31 | public FloatIterable asFloatIterable() 32 | { 33 | int rowCount = this.getDataFrame().rowCount(); 34 | if (rowCount == 0) 35 | { 36 | return FloatLists.immutable.empty(); 37 | } 38 | 39 | return IntInterval.zeroTo(rowCount - 1) 40 | .asLazy() 41 | .collectFloat(this::getFloat); 42 | } 43 | 44 | @Override 45 | public ValueType getType() 46 | { 47 | return ValueType.FLOAT; 48 | } 49 | 50 | @Override 51 | public void addRowToColumn(int rowIndex, DfColumn target) 52 | { 53 | if (this.isNull(rowIndex)) 54 | { 55 | target.addEmptyValue(); 56 | } 57 | else 58 | { 59 | ((DfFloatColumnStored) target).addFloat(this.getFloat(rowIndex)); 60 | } 61 | } 62 | 63 | @Override 64 | public DfColumn mergeWithInto(DfColumn other, DataFrame target) 65 | { 66 | DfFloatColumn mergedCol = (DfFloatColumn) this.validateAndCreateTargetColumn(other, target); 67 | 68 | mergedCol.addAllItemsFrom(this); 69 | mergedCol.addAllItemsFrom((DfFloatColumn) other); 70 | 71 | return mergedCol; 72 | } 73 | 74 | @Override 75 | public DfColumn copyTo(DataFrame target) 76 | { 77 | DfFloatColumn targetCol = (DfFloatColumn) this.copyColumnSchemaAndEnsureCapacity(target); 78 | 79 | targetCol.addAllItemsFrom(this); 80 | return targetCol; 81 | } 82 | 83 | protected abstract void addAllItemsFrom(DfFloatColumn items); 84 | 85 | @Override 86 | public DfCellComparator columnComparator(DfColumn otherColumn) 87 | { 88 | DfFloatColumn otherFloatColumn = (DfFloatColumn) otherColumn; 89 | 90 | return (thisRowIndex, otherRowIndex) -> { 91 | int thisMappedIndex = this.dataFrameRowIndex(thisRowIndex); 92 | int otherMappedIndex = otherFloatColumn.dataFrameRowIndex(otherRowIndex); 93 | 94 | return new DoubleComparisonResult( 95 | () -> this.getFloat(thisMappedIndex), 96 | () -> otherFloatColumn.getFloat(otherMappedIndex), 97 | this.isNull(thisMappedIndex), 98 | otherFloatColumn.isNull(otherMappedIndex) 99 | ); 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfFloatColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.FloatValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 7 | 8 | public class DfFloatColumnComputed 9 | extends DfFloatColumn 10 | implements DfColumnComputed 11 | { 12 | private final String expressionAsString; 13 | private final Expression expression; 14 | 15 | public DfFloatColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 16 | { 17 | super(newDataFrame, newName); 18 | this.expressionAsString = newExpressionAsString; 19 | 20 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 21 | } 22 | 23 | @Override 24 | public Object getObject(int rowIndex) 25 | { 26 | Value result = this.getValue(rowIndex); 27 | 28 | return result.isVoid() ? null : ((FloatValue) result).floatValue(); 29 | } 30 | 31 | @Override 32 | public float getFloat(int rowIndex) 33 | { 34 | Value result = this.getValue(rowIndex); 35 | 36 | if (result.isVoid()) 37 | { 38 | throw new NullPointerException("Null value at " + this.getName() + "[" + rowIndex + "]"); 39 | } 40 | 41 | return ((FloatValue) result).floatValue(); 42 | } 43 | 44 | @Override 45 | protected void addAllItemsFrom(DfFloatColumn floatColumn) 46 | { 47 | this.throwUnmodifiableColumnException(); 48 | } 49 | 50 | @Override 51 | public int getSize() 52 | { 53 | return this.getDataFrame().rowCount(); 54 | } 55 | 56 | @Override 57 | public String getExpressionAsString() 58 | { 59 | return this.expressionAsString; 60 | } 61 | 62 | @Override 63 | public Expression getExpression() 64 | { 65 | return this.expression; 66 | } 67 | 68 | @Override 69 | public boolean isNull(int rowIndex) 70 | { 71 | return this.getObject(rowIndex) == null; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfIntColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.LongComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | import org.eclipse.collections.api.IntIterable; 6 | import org.eclipse.collections.api.list.primitive.ImmutableIntList; 7 | import org.eclipse.collections.impl.factory.primitive.IntLists; 8 | import org.eclipse.collections.impl.list.primitive.IntInterval; 9 | 10 | abstract public class DfIntColumn 11 | extends DfColumnAbstract 12 | { 13 | public DfIntColumn(DataFrame newDataFrame, String newName) 14 | { 15 | super(newDataFrame, newName); 16 | } 17 | 18 | abstract public int getInt(int rowIndex); 19 | 20 | @Override 21 | public String getValueAsString(int rowIndex) 22 | { 23 | return Integer.toString(this.getInt(rowIndex)); 24 | } 25 | 26 | public ImmutableIntList toIntList() 27 | { 28 | return this.asIntIterable().toList().toImmutable(); 29 | } 30 | 31 | public IntIterable asIntIterable() 32 | { 33 | int rowCount = this.getDataFrame().rowCount(); 34 | if (rowCount == 0) 35 | { 36 | return IntLists.immutable.empty(); 37 | } 38 | 39 | return IntInterval 40 | .zeroTo(rowCount - 1) 41 | .asLazy() 42 | .collectInt(this::getInt); 43 | } 44 | 45 | @Override 46 | public ValueType getType() 47 | { 48 | return ValueType.INT; 49 | } 50 | 51 | @Override 52 | public void addRowToColumn(int rowIndex, DfColumn target) 53 | { 54 | if (this.isNull(rowIndex)) 55 | { 56 | target.addEmptyValue(); 57 | } 58 | else 59 | { 60 | ((DfIntColumnStored) target).addInt(this.getInt(rowIndex), false); 61 | } 62 | } 63 | 64 | @Override 65 | public DfColumn mergeWithInto(DfColumn other, DataFrame target) 66 | { 67 | DfIntColumn mergedCol = (DfIntColumn) this.validateAndCreateTargetColumn(other, target); 68 | 69 | mergedCol.addAllItemsFrom(this); 70 | mergedCol.addAllItemsFrom((DfIntColumn) other); 71 | 72 | return mergedCol; 73 | } 74 | 75 | @Override 76 | public DfColumn copyTo(DataFrame target) 77 | { 78 | DfIntColumn targetCol = (DfIntColumn) this.copyColumnSchemaAndEnsureCapacity(target); 79 | 80 | targetCol.addAllItemsFrom(this); 81 | return targetCol; 82 | } 83 | 84 | protected abstract void addAllItemsFrom(DfIntColumn items); 85 | 86 | @Override 87 | public DfCellComparator columnComparator(DfColumn otherColumn) 88 | { 89 | DfIntColumn otherIntColumn = (DfIntColumn) otherColumn; 90 | 91 | return (thisRowIndex, otherRowIndex) -> { 92 | int thisMappedIndex = this.dataFrameRowIndex(thisRowIndex); 93 | int otherMappedIndex = otherIntColumn.dataFrameRowIndex(otherRowIndex); 94 | 95 | return new LongComparisonResult( 96 | () -> this.getInt(thisMappedIndex), 97 | () -> otherIntColumn.getInt(otherMappedIndex), 98 | this.isNull(thisMappedIndex), 99 | otherIntColumn.isNull(otherMappedIndex) 100 | ); 101 | }; 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfIntColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.IntValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 7 | 8 | public class DfIntColumnComputed 9 | extends DfIntColumn 10 | implements DfColumnComputed 11 | { 12 | private final String expressionAsString; 13 | private final Expression expression; 14 | 15 | public DfIntColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 16 | { 17 | super(newDataFrame, newName); 18 | this.expressionAsString = newExpressionAsString; 19 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 20 | } 21 | 22 | @Override 23 | public int getInt(int rowIndex) 24 | { 25 | Value result = this.getValue(rowIndex); 26 | 27 | if (result.isVoid()) 28 | { 29 | throw new NullPointerException("Null value at " + this.getName() + "[" + rowIndex + "]"); 30 | } 31 | 32 | return ((IntValue) result).intValue(); 33 | } 34 | 35 | @Override 36 | public Object getObject(int rowIndex) 37 | { 38 | Value result = this.getValue(rowIndex); 39 | 40 | return result.isVoid() ? null : ((IntValue) result).intValue(); 41 | } 42 | 43 | @Override 44 | public boolean isNull(int rowIndex) 45 | { 46 | return this.getObject(rowIndex) == null; 47 | } 48 | 49 | @Override 50 | protected void addAllItemsFrom(DfIntColumn intColumn) 51 | { 52 | this.throwUnmodifiableColumnException(); 53 | } 54 | 55 | @Override 56 | public int getSize() 57 | { 58 | return this.getDataFrame().rowCount(); 59 | } 60 | 61 | @Override 62 | public String getExpressionAsString() 63 | { 64 | return this.expressionAsString; 65 | } 66 | 67 | @Override 68 | public Expression getExpression() 69 | { 70 | return this.expression; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfIterate.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import org.eclipse.collections.api.block.function.Function; 4 | import org.eclipse.collections.api.block.procedure.Procedure; 5 | import org.eclipse.collections.api.list.ListIterable; 6 | import org.eclipse.collections.impl.factory.Lists; 7 | 8 | import java.util.function.BiConsumer; 9 | import java.util.function.Supplier; 10 | 11 | /** 12 | * An interface defining common iteration patterns for data frames and data frame indices 13 | */ 14 | public interface DfIterate 15 | { 16 | /** 17 | * Executes the procedure for each row in this dataframe iterable 18 | * 19 | * @param action the procedure to execute for each row 20 | */ 21 | void forEach(Procedure action); 22 | 23 | /** 24 | * Creates a list from this dataframe iterable. The new collection is made up of the results of applying 25 | * the specified function to each row of the data frame iterable. 26 | * 27 | * @param action the function the result of which will be collected in the resulting collection 28 | * @param the type of the elements of the resulting collection 29 | * @return the list made up of the results of applying the specified action to each row of the data frame iterable 30 | */ 31 | default ListIterable collect(Function action) 32 | { 33 | return this.collect( 34 | Lists.mutable::empty, 35 | (container, row) -> container.add(action.valueOf(row)) 36 | ); 37 | } 38 | 39 | /** 40 | * Performs a reduction operation on each row of the data frame iterable with the results accumulating in the 41 | * container provided by the supplier parameter. 42 | * @param supplier supplies the result container 43 | * @param accumulator a two argument function that takes the result container and the current row of the data frame 44 | * iterable 45 | * @return the result container 46 | * @param the type of the result reduction operation (result container) 47 | */ 48 | default R collect(Supplier supplier, BiConsumer accumulator) 49 | { 50 | R result = supplier.get(); 51 | 52 | this.forEach(row -> accumulator.accept(result, row)); 53 | 54 | return result; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfLongColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.LongComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | import org.eclipse.collections.api.LongIterable; 6 | import org.eclipse.collections.api.list.primitive.ImmutableLongList; 7 | import org.eclipse.collections.impl.factory.primitive.LongLists; 8 | import org.eclipse.collections.impl.list.primitive.IntInterval; 9 | 10 | abstract public class DfLongColumn 11 | extends DfColumnAbstract 12 | { 13 | public DfLongColumn(DataFrame newDataFrame, String newName) 14 | { 15 | super(newDataFrame, newName); 16 | } 17 | 18 | abstract public long getLong(int rowIndex); 19 | 20 | @Override 21 | public String getValueAsString(int rowIndex) 22 | { 23 | return Long.toString(this.getLong(rowIndex)); 24 | } 25 | 26 | public ImmutableLongList toLongList() 27 | { 28 | return this.asLongIterable().toList().toImmutable(); 29 | } 30 | 31 | public LongIterable asLongIterable() 32 | { 33 | int rowCount = this.getDataFrame().rowCount(); 34 | if (rowCount == 0) 35 | { 36 | return LongLists.immutable.empty(); 37 | } 38 | 39 | return IntInterval.zeroTo(rowCount - 1) 40 | .asLazy() 41 | .collectLong(this::getLong); 42 | } 43 | 44 | @Override 45 | public ValueType getType() 46 | { 47 | return ValueType.LONG; 48 | } 49 | 50 | @Override 51 | public void addRowToColumn(int rowIndex, DfColumn target) 52 | { 53 | if (this.isNull(rowIndex)) 54 | { 55 | target.addEmptyValue(); 56 | } 57 | else 58 | { 59 | ((DfLongColumnStored) target).addLong(this.getLong(rowIndex), false); 60 | } 61 | } 62 | 63 | @Override 64 | public DfColumn mergeWithInto(DfColumn other, DataFrame target) 65 | { 66 | DfLongColumn mergedCol = (DfLongColumn) this.validateAndCreateTargetColumn(other, target); 67 | 68 | mergedCol.addAllItemsFrom(this); 69 | mergedCol.addAllItemsFrom((DfLongColumn) other); 70 | 71 | return mergedCol; 72 | } 73 | 74 | @Override 75 | public DfColumn copyTo(DataFrame target) 76 | { 77 | DfLongColumn targetCol = (DfLongColumn) this.copyColumnSchemaAndEnsureCapacity(target); 78 | 79 | targetCol.addAllItemsFrom(this); 80 | return targetCol; 81 | } 82 | 83 | protected abstract void addAllItemsFrom(DfLongColumn items); 84 | 85 | @Override 86 | public DfCellComparator columnComparator(DfColumn otherColumn) 87 | { 88 | DfLongColumn otherLongColumn = (DfLongColumn) otherColumn; 89 | 90 | return (thisRowIndex, otherRowIndex) -> { 91 | int thisMappedIndex = this.dataFrameRowIndex(thisRowIndex); 92 | int otherMappedIndex = otherLongColumn.dataFrameRowIndex(otherRowIndex); 93 | 94 | return new LongComparisonResult( 95 | () -> this.getLong(thisMappedIndex), 96 | () -> otherLongColumn.getLong(otherMappedIndex), 97 | this.isNull(thisMappedIndex), 98 | otherLongColumn.isNull(otherMappedIndex) 99 | ); 100 | }; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfLongColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 7 | 8 | public class DfLongColumnComputed 9 | extends DfLongColumn 10 | implements DfColumnComputed 11 | { 12 | private final String expressionAsString; 13 | private final Expression expression; 14 | 15 | public DfLongColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 16 | { 17 | super(newDataFrame, newName); 18 | this.expressionAsString = newExpressionAsString; 19 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 20 | } 21 | 22 | @Override 23 | public long getLong(int rowIndex) 24 | { 25 | Value result = this.getValue(rowIndex); 26 | 27 | if (result.isVoid()) 28 | { 29 | throw new NullPointerException("Null value at " + this.getName() + "[" + rowIndex + "]"); 30 | } 31 | 32 | return ((LongValue) result).longValue(); 33 | } 34 | 35 | @Override 36 | public Object getObject(int rowIndex) 37 | { 38 | Value result = this.getValue(rowIndex); 39 | 40 | return result.isVoid() ? null : ((LongValue) result).longValue(); 41 | } 42 | 43 | @Override 44 | public boolean isNull(int rowIndex) 45 | { 46 | return this.getObject(rowIndex) == null; 47 | } 48 | 49 | @Override 50 | protected void addAllItemsFrom(DfLongColumn longColumn) 51 | { 52 | this.throwUnmodifiableColumnException(); 53 | } 54 | 55 | @Override 56 | public int getSize() 57 | { 58 | return this.getDataFrame().rowCount(); 59 | } 60 | 61 | @Override 62 | public String getExpressionAsString() 63 | { 64 | return this.expressionAsString; 65 | } 66 | 67 | @Override 68 | public Expression getExpression() 69 | { 70 | return this.expression; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfObjectColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import org.eclipse.collections.api.block.function.Function2; 5 | import org.eclipse.collections.api.list.ImmutableList; 6 | 7 | public interface DfObjectColumn 8 | extends DfColumn 9 | { 10 | ImmutableList toList(); 11 | 12 | T getTypedObject(int rowIndex); 13 | 14 | Value objectToValue(T anObject); 15 | 16 | @Override 17 | default Object getObject(int rowIndex) 18 | { 19 | return this.getTypedObject(rowIndex); 20 | } 21 | 22 | default IV injectIntoBreakOnNulls( 23 | IV injectedValue, 24 | Function2 function 25 | ) 26 | { 27 | IV result = injectedValue; 28 | 29 | for (int i = 0; i < this.getSize(); i++) 30 | { 31 | T current = this.getTypedObject(i); 32 | if (current == null) 33 | { 34 | return null; 35 | } 36 | 37 | result = function.apply(result, current); 38 | 39 | if (result == null) 40 | { 41 | return null; 42 | } 43 | } 44 | 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfObjectColumnAbstract.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import org.eclipse.collections.api.list.ListIterable; 4 | 5 | abstract public class DfObjectColumnAbstract 6 | extends DfColumnAbstract 7 | implements DfObjectColumn 8 | { 9 | public DfObjectColumnAbstract(DataFrame newDataFrame, String newName) 10 | { 11 | super(newDataFrame, newName); 12 | } 13 | 14 | @Override 15 | public boolean isNull(int rowIndex) 16 | { 17 | return this.getObject(rowIndex) == null; 18 | } 19 | 20 | abstract protected void addAllItems(ListIterable items); 21 | 22 | @Override 23 | public DfColumn mergeWithInto(DfColumn other, DataFrame target) 24 | { 25 | DfObjectColumnAbstract mergedCol = (DfObjectColumnAbstract) this.validateAndCreateTargetColumn(other, target); 26 | 27 | mergedCol.addAllItems(this.toList()); 28 | mergedCol.addAllItems(((DfObjectColumnAbstract) other).toList()); 29 | return mergedCol; 30 | } 31 | 32 | @Override 33 | public DfColumn copyTo(DataFrame target) 34 | { 35 | DfObjectColumnAbstract targetCol = (DfObjectColumnAbstract) this.copyColumnSchemaAndEnsureCapacity(target); 36 | 37 | targetCol.addAllItems(this.toList()); 38 | return targetCol; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfObjectColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 4 | import io.github.vmzakharov.ecdataframe.util.ExpressionParserHelper; 5 | import org.eclipse.collections.api.list.ImmutableList; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | import org.eclipse.collections.impl.factory.Lists; 8 | import org.eclipse.collections.impl.list.primitive.IntInterval; 9 | 10 | abstract public class DfObjectColumnComputed 11 | extends DfObjectColumnAbstract 12 | implements DfColumnComputed 13 | { 14 | private final String expressionAsString; 15 | private final Expression expression; 16 | 17 | public DfObjectColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 18 | { 19 | super(newDataFrame, newName); 20 | this.expressionAsString = newExpressionAsString; 21 | this.expression = ExpressionParserHelper.DEFAULT.toExpressionOrScript(this.expressionAsString); 22 | } 23 | 24 | @Override 25 | public ImmutableList toList() 26 | { 27 | int rowCount = this.getDataFrame().rowCount(); 28 | if (rowCount == 0) 29 | { 30 | return Lists.immutable.empty(); 31 | } 32 | 33 | return IntInterval 34 | .zeroTo(rowCount - 1) 35 | .collect(this::getTypedObject, Lists.mutable.withInitialCapacity(rowCount)) 36 | .toImmutable(); 37 | } 38 | 39 | @Override 40 | public int getSize() 41 | { 42 | return this.getDataFrame().rowCount(); 43 | } 44 | 45 | @Override 46 | public String getExpressionAsString() 47 | { 48 | return this.expressionAsString; 49 | } 50 | 51 | @Override 52 | public Expression getExpression() 53 | { 54 | return this.expression; 55 | } 56 | 57 | @Override 58 | protected void addAllItems(ListIterable items) 59 | { 60 | this.throwUnmodifiableColumnException(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfStringColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.compare.StringComparisonResult; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.StringValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public interface DfStringColumn 9 | extends DfObjectColumn 10 | { 11 | @Override 12 | default String getValueAsString(int rowIndex) 13 | { 14 | return this.getTypedObject(rowIndex); 15 | } 16 | 17 | @Override 18 | default String getValueAsStringLiteral(int rowIndex) 19 | { 20 | String value = this.getTypedObject(rowIndex); 21 | return value == null ? "" : '"' + this.getValueAsString(rowIndex) + '"'; 22 | } 23 | 24 | @Override 25 | default ValueType getType() 26 | { 27 | return ValueType.STRING; 28 | } 29 | 30 | @Override 31 | default Value objectToValue(String anObject) 32 | { 33 | return new StringValue(anObject); 34 | } 35 | 36 | @Override 37 | default void addRowToColumn(int rowIndex, DfColumn target) 38 | { 39 | target.addObject(this.getTypedObject(rowIndex)); 40 | } 41 | 42 | @Override 43 | default DfCellComparator columnComparator(DfColumn otherColumn) 44 | { 45 | DfStringColumn otherStringColumn = (DfStringColumn) otherColumn; 46 | 47 | return (thisRowIndex, otherRowIndex) -> new StringComparisonResult( 48 | this.getTypedObject(this.dataFrameRowIndex(thisRowIndex)), 49 | otherStringColumn.getTypedObject(otherStringColumn.dataFrameRowIndex(otherRowIndex))); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfStringColumnComputed.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | 5 | public class DfStringColumnComputed 6 | extends DfObjectColumnComputed 7 | implements DfStringColumn 8 | { 9 | public DfStringColumnComputed(DataFrame newDataFrame, String newName, String newExpressionAsString) 10 | { 11 | super(newDataFrame, newName, newExpressionAsString); 12 | } 13 | 14 | @Override 15 | public String getTypedObject(int rowIndex) 16 | { 17 | Value result = this.getValue(rowIndex); 18 | 19 | return result.isVoid() ? null : result.stringValue(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfStringColumnStored.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import org.eclipse.collections.api.list.ListIterable; 5 | 6 | public class DfStringColumnStored 7 | extends DfObjectColumnStored 8 | implements DfStringColumn 9 | { 10 | public DfStringColumnStored(DataFrame owner, String newName) 11 | { 12 | super(owner, newName); 13 | } 14 | 15 | public DfStringColumnStored(DataFrame owner, String newName, ListIterable newValues) 16 | { 17 | super(owner, newName, newValues); 18 | } 19 | 20 | @Override 21 | public void addValue(Value value) 22 | { 23 | if (value.isVoid()) 24 | { 25 | this.addObject(null); 26 | } 27 | else if (value.isString()) 28 | { 29 | this.addMyType(value.stringValue()); 30 | } 31 | else 32 | { 33 | this.throwAddingIncompatibleValueException(value); 34 | } 35 | } 36 | 37 | @Override 38 | public void addObject(Object newObject) 39 | { 40 | this.addMyType((String) newObject); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/DfTuple.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import org.eclipse.collections.api.list.ListIterable; 4 | import org.eclipse.collections.impl.utility.ArrayIterate; 5 | 6 | import java.util.Arrays; 7 | 8 | public class DfTuple 9 | implements Comparable 10 | { 11 | private final Object[] items; 12 | private final int order; 13 | 14 | public DfTuple(int newOrder, Object... newItems) 15 | { 16 | this.order = newOrder; 17 | this.items = newItems; 18 | } 19 | 20 | public static int compareMindingNulls(Object thisItem, Object thatItem) 21 | { 22 | if (thisItem == null) 23 | { 24 | return thatItem == null ? 0 : -1; 25 | } 26 | else if (thatItem == null) 27 | { 28 | return 1; 29 | } 30 | 31 | return ((Comparable) thisItem).compareTo(thatItem); 32 | } 33 | 34 | @Override 35 | public boolean equals(Object o) 36 | { 37 | if (o instanceof DfTuple dfTuple) 38 | { 39 | return Arrays.equals(this.items, dfTuple.items); 40 | } 41 | 42 | return false; 43 | } 44 | 45 | @Override 46 | public int hashCode() 47 | { 48 | return Arrays.hashCode(this.items); 49 | } 50 | 51 | @Override 52 | public String toString() 53 | { 54 | return this.order + ":" + ArrayIterate.makeString(this.items); 55 | } 56 | 57 | public int order() 58 | { 59 | return this.order; 60 | } 61 | 62 | @Override 63 | public int compareTo(DfTuple other) 64 | { 65 | return this.compareTo(other, null); 66 | } 67 | 68 | public int compareTo(DfTuple other, ListIterable sortOrders) 69 | { 70 | Object[] theseItems = this.items; 71 | 72 | for (int i = 0; i < theseItems.length; i++) 73 | { 74 | int result = compareMindingNulls(theseItems[i], other.items[i]); 75 | 76 | if (result != 0) 77 | { 78 | return sortOrders == null ? result : sortOrders.get(i).order(result); 79 | } 80 | } 81 | 82 | return 0; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/aggregation/Count.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.aggregation; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.AggregateFunction; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | import org.eclipse.collections.impl.factory.Lists; 8 | 9 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.DATE; 10 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.DATE_TIME; 11 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.DECIMAL; 12 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.DOUBLE; 13 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.FLOAT; 14 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.INT; 15 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.LONG; 16 | import static io.github.vmzakharov.ecdataframe.dsl.value.ValueType.STRING; 17 | 18 | public class Count 19 | extends AggregateFunction 20 | { 21 | private static final ListIterable SUPPORTED_TYPES = Lists.immutable.of(INT, LONG, DOUBLE, FLOAT, STRING, DATE, DATE_TIME, DECIMAL); 22 | 23 | public Count(String newColumnName) 24 | { 25 | super(newColumnName); 26 | } 27 | 28 | public Count(String newColumnName, String newTargetColumnName) 29 | { 30 | super(newColumnName, newTargetColumnName); 31 | } 32 | 33 | @Override 34 | public ValueType targetColumnType(ValueType sourceColumnType) 35 | { 36 | return ValueType.LONG; 37 | } 38 | 39 | @Override 40 | public ListIterable supportedSourceTypes() 41 | { 42 | return SUPPORTED_TYPES; 43 | } 44 | 45 | @Override 46 | public boolean nullsArePoisonous() 47 | { 48 | return false; 49 | } 50 | 51 | @Override 52 | public String getDescription() 53 | { 54 | return "Count the number of rows in a set or a group within a set"; 55 | } 56 | 57 | @Override 58 | public Object applyToColumn(DfColumn column) 59 | { 60 | return column.getSize(); 61 | } 62 | 63 | @Override 64 | public long getLongValue(DfColumn sourceColumn, int sourceRowIndex) 65 | { 66 | return 0; 67 | } 68 | 69 | @Override 70 | protected long longAccumulator(long currentAggregate, long newValue) 71 | { 72 | return currentAggregate + 1; 73 | } 74 | 75 | @Override 76 | public long longInitialValue() 77 | { 78 | return 0; 79 | } 80 | 81 | @Override 82 | public Object valueForEmptyColumn(DfColumn column) 83 | { 84 | return 0; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/BooleanComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.util.function.BooleanSupplier; 4 | 5 | public class BooleanComparisonResult 6 | extends ComparisonResult 7 | { 8 | private int delta; 9 | 10 | public BooleanComparisonResult(BooleanSupplier thisValueSupplier, BooleanSupplier otherValueSupplier, boolean thisIsNull, boolean otherIsNull) 11 | { 12 | this.dealWithNullsIfAny(thisIsNull, otherIsNull); 13 | 14 | if (this.noNulls()) 15 | { 16 | boolean thisValue = thisValueSupplier.getAsBoolean(); 17 | boolean otherValue = otherValueSupplier.getAsBoolean(); 18 | 19 | this.delta(Boolean.compare(thisValue, otherValue)); 20 | } 21 | } 22 | 23 | @Override 24 | public long delta() 25 | { 26 | return this.delta; 27 | } 28 | 29 | @Override 30 | protected void delta(int newDelta) 31 | { 32 | this.delta = newDelta; 33 | } 34 | 35 | @Override 36 | public int compared() 37 | { 38 | return Integer.compare(this.delta, 0); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/DateComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.time.LocalDate; 4 | 5 | public class DateComparisonResult 6 | extends ObjectComparisonResult 7 | { 8 | public DateComparisonResult(LocalDate thisObject, LocalDate thatObject) 9 | { 10 | super(thisObject, thatObject); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/DateTimeComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.time.LocalDateTime; 4 | 5 | public class DateTimeComparisonResult 6 | extends ObjectComparisonResult 7 | { 8 | public DateTimeComparisonResult(LocalDateTime thisObject, LocalDateTime thatObject) 9 | { 10 | super(thisObject, thatObject); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/DecimalComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public class DecimalComparisonResult 6 | extends ComparisonResult 7 | { 8 | private BigDecimal decDelta; 9 | 10 | public DecimalComparisonResult(BigDecimal thisObject, BigDecimal thatObject) 11 | { 12 | this.dealWithNullsIfAny(thisObject == null, thatObject == null); 13 | 14 | if (this.noNulls()) 15 | { 16 | this.decDelta = thisObject.subtract(thatObject); 17 | } 18 | } 19 | 20 | @Override 21 | public double dDelta() 22 | { 23 | return this.decDelta.doubleValue(); 24 | } 25 | 26 | public BigDecimal decDelta() 27 | { 28 | return this.decDelta; 29 | } 30 | 31 | @Override 32 | public int compared() 33 | { 34 | return this.decDelta.signum(); 35 | } 36 | 37 | @Override 38 | public long delta() 39 | { 40 | return this.decDelta.longValue(); 41 | } 42 | 43 | @Override 44 | protected void delta(int newDelta) 45 | { 46 | this.decDelta = BigDecimal.valueOf(newDelta); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/DoubleComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.util.function.DoubleSupplier; 4 | 5 | public class DoubleComparisonResult 6 | extends ComparisonResult 7 | { 8 | private double delta; 9 | 10 | public DoubleComparisonResult(DoubleSupplier thisValueSupplier, DoubleSupplier otherValueSupplier, boolean thisIsNull, boolean otherIsNull) 11 | { 12 | this.dealWithNullsIfAny(thisIsNull, otherIsNull); 13 | 14 | if (this.noNulls()) 15 | { 16 | double thisValue = thisValueSupplier.getAsDouble(); 17 | double otherValue = otherValueSupplier.getAsDouble(); 18 | this.delta = thisValue - otherValue; 19 | } 20 | } 21 | 22 | @Override 23 | public long delta() 24 | { 25 | return Math.round(this.delta); 26 | } 27 | 28 | @Override 29 | public double dDelta() 30 | { 31 | return this.delta; 32 | } 33 | 34 | @Override 35 | protected void delta(int newDelta) 36 | { 37 | this.delta = newDelta; 38 | } 39 | 40 | @Override 41 | public int compared() 42 | { 43 | return Double.compare(this.delta, 0.0); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/LongComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | import java.util.function.LongSupplier; 4 | 5 | public class LongComparisonResult 6 | extends ComparisonResult 7 | { 8 | private long delta; 9 | 10 | public LongComparisonResult(LongSupplier thisValueSupplier, LongSupplier otherValueSupplier, boolean thisIsNull, boolean otherIsNull) 11 | { 12 | this.dealWithNullsIfAny(thisIsNull, otherIsNull); 13 | 14 | if (this.noNulls()) 15 | { 16 | long thisValue = thisValueSupplier.getAsLong(); 17 | long otherValue = otherValueSupplier.getAsLong(); 18 | this.delta = thisValue - otherValue; 19 | 20 | // check for overflows 21 | if ((thisValue > otherValue) && (this.delta < 0)) 22 | { 23 | this.delta = Long.MAX_VALUE; 24 | } 25 | else if ((thisValue < otherValue) && (this.delta > 0)) 26 | { 27 | this.delta = Long.MIN_VALUE; 28 | } 29 | } 30 | } 31 | 32 | @Override 33 | public long delta() 34 | { 35 | return this.delta; 36 | } 37 | 38 | @Override 39 | protected void delta(int newDelta) 40 | { 41 | this.delta = newDelta; 42 | } 43 | 44 | @Override 45 | public int compared() 46 | { 47 | return this.delta == 0 ? 0 : (this.delta > 0 ? 1 : -1); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/ObjectComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | abstract public class ObjectComparisonResult> 4 | extends ComparisonResult 5 | { 6 | private int delta; 7 | 8 | public ObjectComparisonResult(T thisObject, T thatObject) 9 | { 10 | this.dealWithNullsIfAny(thisObject == null, thatObject == null); 11 | 12 | if (this.noNulls()) 13 | { 14 | this.delta(thisObject.compareTo(thatObject)); 15 | } 16 | } 17 | 18 | @Override 19 | public long delta() 20 | { 21 | return this.delta; 22 | } 23 | 24 | @Override 25 | protected void delta(int newDelta) 26 | { 27 | this.delta = newDelta; 28 | } 29 | 30 | @Override 31 | public int compared() 32 | { 33 | return this.delta; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataframe/compare/StringComparisonResult.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe.compare; 2 | 3 | public class StringComparisonResult 4 | extends ObjectComparisonResult 5 | { 6 | public StringComparisonResult(String thisObject, String thatObject) 7 | { 8 | super(thisObject, thatObject); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/BooleanFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.block.function.primitive.BooleanFunction; 4 | import org.eclipse.collections.impl.utility.StringIterate; 5 | 6 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 7 | 8 | public class BooleanFormatter 9 | { 10 | private final BooleanFunction booleanParser; 11 | private final String trueString; 12 | private final String falseString; 13 | 14 | public BooleanFormatter(String pattern) 15 | { 16 | if (StringIterate.isEmpty(pattern)) 17 | { 18 | this.trueString = "true"; 19 | this.falseString = "false"; 20 | } 21 | else if (pattern.contains(",")) 22 | { 23 | String[] elements = pattern.split(","); 24 | this.trueString = elements[0]; 25 | this.falseString = elements[1]; 26 | } 27 | else 28 | { 29 | throw exceptionByKey("CSV_INVALID_FORMAT_STR") 30 | .with("format", pattern) 31 | .with("type", "boolean") 32 | .get(); 33 | } 34 | 35 | this.booleanParser = s -> { 36 | if (this.trueString.equals(s)) return true; 37 | if (this.falseString.equals(s)) return false; 38 | 39 | throw exceptionByKey("CSV_PARSE_ERR") 40 | .with("type", "boolean") 41 | .with("inputString", s) 42 | .get(); 43 | }; 44 | } 45 | 46 | public boolean parseAsBoolean(String aString) 47 | { 48 | if (aString == null || aString.isEmpty()) 49 | { 50 | return false; 51 | } 52 | 53 | return this.booleanParser.booleanValueOf(aString); 54 | } 55 | 56 | public String format(boolean booleanValue) 57 | { 58 | return booleanValue ? this.trueString : this.falseString; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/BooleanSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfBooleanColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfBooleanColumnStored; 5 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public class BooleanSchemaColumn 9 | extends CsvSchemaColumn 10 | { 11 | final transient private BooleanFormatter booleanFormatter; 12 | 13 | public BooleanSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 14 | { 15 | super(newCsvSchema, newName, ValueType.BOOLEAN, newPattern); 16 | 17 | this.booleanFormatter = new BooleanFormatter(this.getPattern()); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | boolean booleanValue = ((DfBooleanColumn) column).getBoolean(rowIndex); 24 | return this.booleanFormatter.format(booleanValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | if (aString == null) 31 | { 32 | dfColumn.addEmptyValue(); 33 | return; 34 | } 35 | 36 | ((DfBooleanColumnStored) dfColumn).addBoolean(this.booleanFormatter.parseAsBoolean(aString), false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/CsvSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | 6 | public abstract class CsvSchemaColumn 7 | { 8 | private final String name; 9 | private final ValueType type; 10 | private String pattern; 11 | 12 | transient private final CsvSchema csvSchema; 13 | 14 | public CsvSchemaColumn(CsvSchema newCsvSchema, String newName, ValueType newType, String newPattern) 15 | { 16 | this.csvSchema = newCsvSchema; 17 | this.name = newName; 18 | this.type = newType; 19 | this.pattern = newPattern; 20 | } 21 | 22 | protected CsvSchema csvSchema() 23 | { 24 | return this.csvSchema; 25 | } 26 | 27 | public String getName() 28 | { 29 | return this.name; 30 | } 31 | 32 | public ValueType getType() 33 | { 34 | return this.type; 35 | } 36 | 37 | public String getPattern() 38 | { 39 | return this.pattern; 40 | } 41 | 42 | protected void setPattern(String newPattern) 43 | { 44 | this.pattern = newPattern; 45 | } 46 | 47 | abstract public void parseAndAddToColumn(String aString, DfColumn dfColumn); 48 | 49 | abstract public String formatColumnValueAsString(DfColumn column, int rowIndex); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DataSet.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | public interface DataSet 4 | { 5 | void openFileForReading(); 6 | 7 | String getName(); 8 | 9 | /** 10 | * Returns the next element in the data set. 11 | * @return the next element in the data set 12 | */ 13 | Object next(); 14 | 15 | boolean hasNext(); 16 | 17 | void close(); 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DataSetAbstract.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | abstract public class DataSetAbstract 4 | implements DataSet 5 | { 6 | private final String name; 7 | 8 | public DataSetAbstract(String newName) 9 | { 10 | this.name = newName; 11 | } 12 | 13 | @Override 14 | public String getName() 15 | { 16 | return this.name; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DateSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfDateColumn; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 6 | 7 | import java.time.LocalDate; 8 | import java.time.format.DateTimeFormatter; 9 | import java.time.format.ResolverStyle; 10 | 11 | public class DateSchemaColumn 12 | extends CsvSchemaColumn 13 | { 14 | final transient private DateTimeFormatter dateTimeFormatter; 15 | 16 | public DateSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 17 | { 18 | super(newCsvSchema, newName, ValueType.DATE, newPattern); 19 | 20 | if (newPattern == null) 21 | { 22 | this.setPattern("uuuu-M-d"); 23 | } 24 | 25 | this.dateTimeFormatter = DateTimeFormatter.ofPattern(this.getPattern()).withResolverStyle(ResolverStyle.STRICT); 26 | } 27 | 28 | @Override 29 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 30 | { 31 | LocalDate dateValue = ((DfDateColumn) column).getTypedObject(rowIndex); 32 | return this.dateTimeFormatter.format(dateValue); 33 | } 34 | 35 | @Override 36 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 37 | { 38 | dfColumn.addObject(this.parseAsLocalDate(aString)); 39 | } 40 | 41 | private LocalDate parseAsLocalDate(String aString) 42 | { 43 | if (aString == null) 44 | { 45 | return null; 46 | } 47 | 48 | String trimmed = aString.trim(); 49 | 50 | return trimmed.isEmpty() ? null : LocalDate.parse(trimmed, this.dateTimeFormatter); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DateTimeSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfDateTimeColumn; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 6 | 7 | import java.time.LocalDateTime; 8 | import java.time.format.DateTimeFormatter; 9 | import java.time.format.ResolverStyle; 10 | 11 | public class DateTimeSchemaColumn 12 | extends CsvSchemaColumn 13 | { 14 | final transient private DateTimeFormatter dateTimeFormatter; 15 | 16 | public DateTimeSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 17 | { 18 | super(newCsvSchema, newName, ValueType.DATE_TIME, newPattern); 19 | 20 | if (newPattern == null) 21 | { 22 | this.setPattern("uuuu-M-d'T'H:m:s"); 23 | } 24 | 25 | this.dateTimeFormatter = DateTimeFormatter.ofPattern(this.getPattern()).withResolverStyle(ResolverStyle.STRICT); 26 | } 27 | 28 | @Override 29 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 30 | { 31 | LocalDateTime dateTimeValue = ((DfDateTimeColumn) column).getTypedObject(rowIndex); 32 | return this.dateTimeFormatter.format(dateTimeValue); 33 | } 34 | 35 | @Override 36 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 37 | { 38 | dfColumn.addObject(this.parseAsLocalDateTime(aString)); 39 | } 40 | 41 | private LocalDateTime parseAsLocalDateTime(String aString) 42 | { 43 | if (aString == null) 44 | { 45 | return null; 46 | } 47 | 48 | String trimmed = aString.trim(); 49 | 50 | return trimmed.isEmpty() ? null : LocalDateTime.parse(trimmed, this.dateTimeFormatter); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DecimalFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.block.function.Function; 4 | import org.eclipse.collections.impl.utility.StringIterate; 5 | 6 | import java.math.BigDecimal; 7 | import java.text.DecimalFormat; 8 | import java.text.ParseException; 9 | import java.util.regex.Pattern; 10 | 11 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 12 | 13 | public class DecimalFormatter 14 | { 15 | private DecimalFormat decimalFormat; 16 | private final Function decimalParser; 17 | private Pattern nonNumericPattern; 18 | 19 | public DecimalFormatter(String pattern) 20 | { 21 | if (StringIterate.isEmpty(pattern)) 22 | { 23 | this.decimalParser = BigDecimal::new; 24 | } 25 | else if (pattern.length() == 1) 26 | { 27 | this.nonNumericPattern = Pattern.compile("[^0-9" + pattern + "]"); 28 | this.decimalParser = s -> new BigDecimal(this.stripNonNumericCharacters(s).replace(pattern, ".")); 29 | } 30 | else 31 | { 32 | this.decimalFormat = new DecimalFormat(pattern); 33 | this.decimalFormat.setParseBigDecimal(true); 34 | 35 | this.decimalParser = s -> { 36 | try 37 | { 38 | return (BigDecimal) this.decimalFormat.parse(s.trim()); 39 | } 40 | catch (ParseException e) 41 | { 42 | throw exceptionByKey("CSV_PARSE_ERR") 43 | .with("type", "a floating point number") 44 | .with("inputString", s).get(e); 45 | } 46 | }; 47 | } 48 | } 49 | 50 | public BigDecimal parseAsDecimal(String aString) 51 | { 52 | if (aString == null || aString.isEmpty()) 53 | { 54 | return null; 55 | } 56 | 57 | return this.decimalParser.apply(aString); 58 | } 59 | 60 | private String stripNonNumericCharacters(String s) 61 | { 62 | return this.nonNumericPattern.matcher(s).replaceAll(""); 63 | } 64 | 65 | public String format(BigDecimal decimalValue) 66 | { 67 | return this.decimalFormat == null ? String.valueOf(decimalValue) : this.decimalFormat.format(decimalValue); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DecimalSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfDecimalColumn; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public class DecimalSchemaColumn 10 | extends CsvSchemaColumn 11 | { 12 | final transient private DecimalFormatter decimalFormatter; 13 | 14 | public DecimalSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 15 | { 16 | super(newCsvSchema, newName, ValueType.DECIMAL, newPattern); 17 | this.decimalFormatter = new DecimalFormatter(newPattern); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | BigDecimal decimalValue = ((DfDecimalColumn) column).getTypedObject(rowIndex); 24 | return this.decimalFormatter.format(decimalValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | dfColumn.addObject(this.decimalFormatter.parseAsDecimal(aString)); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DoubleFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.block.function.primitive.DoubleFunction; 4 | import org.eclipse.collections.impl.utility.StringIterate; 5 | 6 | import java.text.DecimalFormat; 7 | import java.text.ParseException; 8 | import java.util.regex.Pattern; 9 | 10 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 11 | 12 | public class DoubleFormatter 13 | { 14 | private DecimalFormat decimalFormat; 15 | private DoubleFunction doubleParser; 16 | private Pattern nonNumericPattern; 17 | 18 | public DoubleFormatter(String pattern) 19 | { 20 | if (StringIterate.isEmpty(pattern)) 21 | { 22 | this.doubleParser = Double::parseDouble; 23 | } 24 | else if (pattern.length() == 1) 25 | { 26 | this.nonNumericPattern = Pattern.compile("[^0-9" + pattern + "]"); 27 | this.doubleParser = s -> Double.parseDouble(this.stripNonNumericCharacters(s).replace(pattern, ".")); 28 | } 29 | else 30 | { 31 | this.decimalFormat = new DecimalFormat(pattern); 32 | this.doubleParser = s -> { 33 | try 34 | { 35 | return this.decimalFormat.parse(s.trim()).doubleValue(); 36 | } 37 | catch (ParseException e) 38 | { 39 | throw exceptionByKey("CSV_PARSE_ERR") 40 | .with("type", "a floating point number") 41 | .with("inputString", s).get(e); 42 | } 43 | }; 44 | } 45 | } 46 | 47 | public double parseAsDouble(String aString) 48 | { 49 | if (aString == null || aString.isEmpty()) 50 | { 51 | return 0.0; 52 | } 53 | 54 | return this.doubleParser.applyAsDouble(aString); 55 | } 56 | 57 | private String stripNonNumericCharacters(String s) 58 | { 59 | return this.nonNumericPattern.matcher(s).replaceAll(""); 60 | } 61 | 62 | public String format(double doubleValue) 63 | { 64 | return this.decimalFormat == null ? Double.toString(doubleValue) : this.decimalFormat.format(doubleValue); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/DoubleSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfDoubleColumn; 5 | import io.github.vmzakharov.ecdataframe.dataframe.DfDoubleColumnStored; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public class DoubleSchemaColumn 9 | extends CsvSchemaColumn 10 | { 11 | final transient private DoubleFormatter doubleFormatter; 12 | 13 | public DoubleSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 14 | { 15 | super(newCsvSchema, newName, ValueType.DOUBLE, newPattern); 16 | 17 | this.doubleFormatter = new DoubleFormatter(this.getPattern()); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | double doubleValue = ((DfDoubleColumn) column).getDouble(rowIndex); 24 | return this.doubleFormatter.format(doubleValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | if (aString == null) 31 | { 32 | dfColumn.addEmptyValue(); 33 | return; 34 | } 35 | 36 | ((DfDoubleColumnStored) dfColumn).addDouble(this.doubleFormatter.parseAsDouble(aString)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/FloatFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.block.function.primitive.FloatFunction; 4 | import org.eclipse.collections.impl.utility.StringIterate; 5 | 6 | import java.text.DecimalFormat; 7 | import java.text.ParseException; 8 | import java.util.regex.Pattern; 9 | 10 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 11 | 12 | public class FloatFormatter 13 | { 14 | private DecimalFormat decimalFormat; 15 | private final FloatFunction floatParser; 16 | private Pattern nonNumericPattern; 17 | 18 | public FloatFormatter(String pattern) 19 | { 20 | if (StringIterate.isEmpty(pattern)) 21 | { 22 | this.floatParser = Float::parseFloat; 23 | } 24 | else if (pattern.length() == 1) 25 | { 26 | this.nonNumericPattern = Pattern.compile("[^0-9" + pattern + "]"); 27 | this.floatParser = s -> Float.parseFloat(this.stripNonNumericCharacters(s).replace(pattern, ".")); 28 | } 29 | else 30 | { 31 | this.decimalFormat = new DecimalFormat(pattern); 32 | this.floatParser = s -> { 33 | try 34 | { 35 | return this.decimalFormat.parse(s.trim()).floatValue(); 36 | } 37 | catch (ParseException e) 38 | { 39 | throw exceptionByKey("CSV_PARSE_ERR") 40 | .with("type", "a floating point number") 41 | .with("inputString", s).get(e); 42 | } 43 | }; 44 | } 45 | } 46 | 47 | public float parseAsFloat(String aString) 48 | { 49 | if (aString == null || aString.isEmpty()) 50 | { 51 | return 0.0f; 52 | } 53 | 54 | return this.floatParser.floatValueOf(aString); 55 | } 56 | 57 | private String stripNonNumericCharacters(String s) 58 | { 59 | return this.nonNumericPattern.matcher(s).replaceAll(""); 60 | } 61 | 62 | public String format(float floatValue) 63 | { 64 | return this.decimalFormat == null ? Float.toString(floatValue) : this.decimalFormat.format(floatValue); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/FloatSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfFloatColumn; 5 | import io.github.vmzakharov.ecdataframe.dataframe.DfFloatColumnStored; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public class FloatSchemaColumn 9 | extends CsvSchemaColumn 10 | { 11 | final transient private FloatFormatter floatFormatter; 12 | 13 | public FloatSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 14 | { 15 | super(newCsvSchema, newName, ValueType.FLOAT, newPattern); 16 | 17 | this.floatFormatter = new FloatFormatter(this.getPattern()); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | float floatValue = ((DfFloatColumn) column).getFloat(rowIndex); 24 | return this.floatFormatter.format(floatValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | if (aString == null) 31 | { 32 | dfColumn.addEmptyValue(); 33 | return; 34 | } 35 | 36 | ((DfFloatColumnStored) dfColumn).addFloat(this.floatFormatter.parseAsFloat(aString)); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/HierarchicalDataSet.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 4 | 5 | abstract public class HierarchicalDataSet 6 | extends DataSetAbstract 7 | { 8 | public HierarchicalDataSet(String newName) 9 | { 10 | super(newName); 11 | } 12 | 13 | abstract public Object getValue(String propertyChainString); 14 | 15 | abstract public ValueType getFieldType(String fieldName); 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/IntFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import java.text.DecimalFormat; 4 | import java.text.ParseException; 5 | import java.util.regex.Pattern; 6 | 7 | import org.eclipse.collections.api.block.function.primitive.IntFunction; 8 | import org.eclipse.collections.impl.utility.StringIterate; 9 | 10 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 11 | 12 | public class IntFormatter 13 | { 14 | private DecimalFormat decimalFormat; 15 | private IntFunction intParser; 16 | private Pattern nonNumericPattern; 17 | 18 | public IntFormatter(String pattern) 19 | { 20 | if (StringIterate.isEmpty(pattern)) 21 | { 22 | this.intParser = Integer::parseInt; 23 | } 24 | else if (pattern.length() == 1) 25 | { 26 | this.nonNumericPattern = Pattern.compile("[^0-9" + pattern + "]"); 27 | this.intParser = s -> Integer.parseInt(this.stripNonNumericCharacters(s)); 28 | } 29 | else 30 | { 31 | this.decimalFormat = new DecimalFormat(pattern); 32 | this.intParser = s -> { 33 | try 34 | { 35 | return this.decimalFormat.parse(s.trim()).intValue(); 36 | } 37 | catch (ParseException e) 38 | { 39 | throw exceptionByKey("CSV_PARSE_ERR") 40 | .with("type", "integer") 41 | .with("inputString", s) 42 | .get(e); 43 | } 44 | }; 45 | } 46 | } 47 | 48 | public int parseAsInt(String aString) 49 | { 50 | if (aString == null || aString.isEmpty()) 51 | { 52 | return 0; 53 | } 54 | 55 | return this.intParser.applyAsInt(aString); 56 | } 57 | 58 | public String format(int intValue) 59 | { 60 | return this.decimalFormat == null ? Integer.toString(intValue) : this.decimalFormat.format(intValue); 61 | } 62 | 63 | private String stripNonNumericCharacters(String s) 64 | { 65 | return this.nonNumericPattern.matcher(s).replaceAll(""); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/IntSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfIntColumn; 5 | import io.github.vmzakharov.ecdataframe.dataframe.DfIntColumnStored; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public class IntSchemaColumn 9 | extends CsvSchemaColumn 10 | { 11 | final transient private IntFormatter intFormatter; 12 | 13 | public IntSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 14 | { 15 | super(newCsvSchema, newName, ValueType.INT, newPattern); 16 | 17 | this.intFormatter = new IntFormatter(this.getPattern()); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | int intValue = ((DfIntColumn) column).getInt(rowIndex); 24 | return this.intFormatter.format(intValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | if (aString == null) 31 | { 32 | dfColumn.addEmptyValue(); 33 | return; 34 | } 35 | 36 | ((DfIntColumnStored) dfColumn).addInt(this.intFormatter.parseAsInt(aString), false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/LongFormatter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.block.function.primitive.LongFunction; 4 | import org.eclipse.collections.impl.utility.StringIterate; 5 | 6 | import java.text.DecimalFormat; 7 | import java.text.ParseException; 8 | import java.util.regex.Pattern; 9 | 10 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 11 | 12 | public class LongFormatter 13 | { 14 | private DecimalFormat decimalFormat; 15 | private LongFunction longParser; 16 | private Pattern nonNumericPattern; 17 | 18 | public LongFormatter(String pattern) 19 | { 20 | if (StringIterate.isEmpty(pattern)) 21 | { 22 | this.longParser = Long::parseLong; 23 | } 24 | else if (pattern.length() == 1) 25 | { 26 | this.nonNumericPattern = Pattern.compile("[^0-9" + pattern + "]"); 27 | this.longParser = s -> Long.parseLong(this.stripNonNumericCharacters(s)); 28 | } 29 | else 30 | { 31 | this.decimalFormat = new DecimalFormat(pattern); 32 | this.longParser = s -> { 33 | try 34 | { 35 | return this.decimalFormat.parse(s.trim()).longValue(); 36 | } 37 | catch (ParseException e) 38 | { 39 | throw exceptionByKey("CSV_PARSE_ERR") 40 | .with("type", "integer") 41 | .with("inputString", s) 42 | .get(e); 43 | } 44 | }; 45 | } 46 | } 47 | 48 | public long parseAsLong(String aString) 49 | { 50 | if (aString == null || aString.isEmpty()) 51 | { 52 | return 0L; 53 | } 54 | 55 | return this.longParser.applyAsLong(aString); 56 | } 57 | 58 | public String format(long longValue) 59 | { 60 | return this.decimalFormat == null ? Long.toString(longValue) : this.decimalFormat.format(longValue); 61 | } 62 | 63 | private String stripNonNumericCharacters(String s) 64 | { 65 | return this.nonNumericPattern.matcher(s).replaceAll(""); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/LongSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dataframe.DfLongColumn; 5 | import io.github.vmzakharov.ecdataframe.dataframe.DfLongColumnStored; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | 8 | public class LongSchemaColumn 9 | extends CsvSchemaColumn 10 | { 11 | final transient private LongFormatter longFormatter; 12 | 13 | public LongSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 14 | { 15 | super(newCsvSchema, newName, ValueType.LONG, newPattern); 16 | 17 | this.longFormatter = new LongFormatter(this.getPattern()); 18 | } 19 | 20 | @Override 21 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 22 | { 23 | long longValue = ((DfLongColumn) column).getLong(rowIndex); 24 | return this.longFormatter.format(longValue); 25 | } 26 | 27 | @Override 28 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 29 | { 30 | if (aString == null) 31 | { 32 | dfColumn.addEmptyValue(); 33 | return; 34 | } 35 | 36 | ((DfLongColumnStored) dfColumn).addLong(this.longFormatter.parseAsLong(aString), false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dataset/StringSchemaColumn.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DfColumn; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | 6 | public class StringSchemaColumn 7 | extends CsvSchemaColumn 8 | { 9 | public StringSchemaColumn(CsvSchema newCsvSchema, String newName, String newPattern) 10 | { 11 | super(newCsvSchema, newName, ValueType.STRING, newPattern); 12 | } 13 | 14 | @Override 15 | public String formatColumnValueAsString(DfColumn column, int rowIndex) 16 | { 17 | String stringValue = column.getValueAsString(rowIndex); 18 | return this.csvSchema().getQuoteCharacter() + stringValue + this.csvSchema().getQuoteCharacter(); 19 | } 20 | 21 | @Override 22 | public void parseAndAddToColumn(String aString, DfColumn dfColumn) 23 | { 24 | dfColumn.addObject(this.stripQuotesIfAny(aString)); 25 | } 26 | 27 | private String stripQuotesIfAny(String aString) 28 | { 29 | if (aString == null || !this.csvSchema().surroundedByQuotes(aString)) 30 | { 31 | return aString; 32 | } 33 | 34 | return aString.substring(1, aString.length() - 1); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/AbstractScript.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import org.eclipse.collections.api.list.ListIterable; 4 | import org.eclipse.collections.api.list.MutableList; 5 | import org.eclipse.collections.impl.factory.Lists; 6 | 7 | /** 8 | * An abstract superclass for the {@code Script} hierarchy, adds behavior to keep track of the script's expressions 9 | * (statements) 10 | */ 11 | abstract public class AbstractScript 12 | implements Script 13 | { 14 | private final MutableList expressions = Lists.mutable.of(); 15 | 16 | @Override 17 | public Expression addExpression(Expression anExpression) 18 | { 19 | this.expressions.add(anExpression); 20 | return anExpression; 21 | } 22 | 23 | @Override 24 | public ListIterable getExpressions() 25 | { 26 | return this.expressions; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/AliasExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record AliasExpr(String alias, Expression expression) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 13 | { 14 | return this.expression.evaluate(evaluationVisitor); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitAliasExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/AnonymousScript.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import org.eclipse.collections.api.map.MutableMap; 7 | import org.eclipse.collections.impl.factory.Maps; 8 | 9 | public class AnonymousScript 10 | extends AbstractScript 11 | { 12 | private final MutableMap functions = Maps.mutable.of(); 13 | 14 | public AnonymousScript() 15 | { 16 | } 17 | 18 | @Override 19 | public Value evaluate(ExpressionEvaluationVisitor visitor) 20 | { 21 | return visitor.visitAnonymousScriptExpr(this); 22 | } 23 | 24 | @Override 25 | public void accept(ExpressionVisitor visitor) 26 | { 27 | visitor.visitAnonymousScriptExpr(this); 28 | } 29 | 30 | public void addFunctionScript(FunctionScript functionScript) 31 | { 32 | this.functions.put(functionScript.getNormalizedName(), functionScript); 33 | } 34 | 35 | public MutableMap getFunctions() 36 | { 37 | return this.functions; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/AssignExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record AssignExpr(String varName, boolean isEscaped, Expression expression) 8 | implements Expression 9 | { 10 | @Override 11 | public Value evaluate(ExpressionEvaluationVisitor visitor) 12 | { 13 | return visitor.visitAssignExpr(this); 14 | } 15 | 16 | @Override 17 | public void accept(ExpressionVisitor visitor) 18 | { 19 | visitor.visitAssignExpr(this); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/BinaryExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record BinaryExpr(Expression operand1, Expression operand2, BinaryOp operation) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor visitor) 13 | { 14 | return visitor.visitBinaryExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitBinaryExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/BinaryOp.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | 5 | import java.util.function.Supplier; 6 | 7 | /** 8 | * A binary operation in the expression DSL 9 | */ 10 | public interface BinaryOp 11 | { 12 | /** 13 | * Applies this operation to the operands 14 | * @param operand1 the first operand 15 | * @param operand2 the second operand 16 | * @return the result of applying this operation 17 | */ 18 | Value apply(Value operand1, Value operand2); 19 | 20 | /** 21 | * Override this method if partial evaluation of operands is possible to determine the outcome of the operation, 22 | * e.g. "and" and "or" operators. 23 | * @param operand1 a supplier of the first value 24 | * @param operand2 a supplier of the second value 25 | * @return the result of applying the operation 26 | */ 27 | default Value apply(Supplier operand1, Supplier operand2) 28 | { 29 | return this.apply(operand1.get(), operand2.get()); 30 | } 31 | 32 | /** 33 | * The parseable string representation of this operation parseable 34 | * @return the string representation of this operation 35 | */ 36 | String asString(); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/ContainsOp.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.BooleanValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.VectorValue; 6 | 7 | public interface ContainsOp 8 | extends PredicateOp 9 | { 10 | ContainsOp IN = new ContainsOp() 11 | { 12 | @Override 13 | public BooleanValue applyWithVector(Value value, VectorValue vectorValue) 14 | { 15 | return BooleanValue.valueOf(vectorValue.getElements().anySatisfy(e -> ComparisonOp.EQ.apply(e, value).isTrue())); 16 | } 17 | 18 | @Override 19 | public BooleanValue applyString(String operand1, String operand2) 20 | { 21 | return BooleanValue.valueOf(operand2.contains(operand1)); 22 | } 23 | 24 | @Override 25 | public String asString() 26 | { 27 | return "in"; 28 | } 29 | }; 30 | 31 | ContainsOp NOT_IN = new ContainsOp() 32 | { 33 | @Override 34 | public BooleanValue applyWithVector(Value value, VectorValue vectorValue) 35 | { 36 | return BooleanValue.valueOf(vectorValue.getElements().allSatisfy(e -> ComparisonOp.NE.apply(e, value).isTrue())); 37 | } 38 | 39 | @Override 40 | public BooleanValue applyString(String operand1, String operand2) 41 | { 42 | return BooleanValue.valueOf(!operand2.contains(operand1)); 43 | } 44 | 45 | @Override 46 | public String asString() 47 | { 48 | return "not in"; 49 | } 50 | }; 51 | 52 | @Override 53 | default BooleanValue apply(Value operand1, Value operand2) 54 | { 55 | if (operand2.isVector()) 56 | { 57 | return this.applyWithVector(operand1, (VectorValue) operand2); 58 | } 59 | return operand1.applyPredicate(operand2, this); 60 | } 61 | 62 | BooleanValue applyWithVector(Value value, VectorValue vectorValue); 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/DecimalExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record DecimalExpr(Expression unscaledValueExpr, Expression scaleExpr) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 13 | { 14 | return evaluationVisitor.visitDecimalExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitDecimalExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/EvalContext.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataset.HierarchicalDataSet; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import org.eclipse.collections.api.RichIterable; 6 | import org.eclipse.collections.api.map.MapIterable; 7 | import org.eclipse.collections.api.map.MutableMap; 8 | 9 | import java.time.LocalDate; 10 | 11 | public interface EvalContext 12 | { 13 | Value setVariable(String newVarName, Value newValue); 14 | 15 | Value getVariable(String newVariableName); 16 | 17 | Value getVariableOrDefault(String newVariableName, Value defaultValue); 18 | 19 | boolean hasVariable(String variableName); 20 | 21 | void removeVariable(String variableName); 22 | 23 | MapIterable getDeclaredFunctions(); 24 | 25 | void setDeclaredFunctions(MutableMap newDeclaredFunctions); 26 | 27 | FunctionScript getDeclaredFunction(String functionName); 28 | 29 | void addDataSet(HierarchicalDataSet dataSet); 30 | 31 | HierarchicalDataSet getDataSet(String dataSetName); 32 | 33 | RichIterable getVariableNames(); 34 | 35 | void removeAllVariables(); 36 | 37 | String getString(String variableName); 38 | 39 | long getLong(String variableName); 40 | 41 | LocalDate getDate(String variableName); 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/EvalContextAbstract.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DateValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import org.eclipse.collections.api.map.MapIterable; 7 | import org.eclipse.collections.api.map.MutableMap; 8 | import org.eclipse.collections.impl.factory.Maps; 9 | 10 | import java.time.LocalDate; 11 | 12 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 13 | 14 | abstract public class EvalContextAbstract 15 | implements EvalContext 16 | { 17 | private MutableMap declaredFunctions = Maps.mutable.of(); 18 | private final MutableMap contextVariables = Maps.mutable.of(); 19 | 20 | @Override 21 | public boolean hasVariable(String variableName) 22 | { 23 | return this.contextVariables.containsKey(variableName); 24 | } 25 | 26 | @Override 27 | public void removeVariable(String variableName) 28 | { 29 | this.contextVariables.remove(variableName); 30 | } 31 | 32 | @Override 33 | public void removeAllVariables() 34 | { 35 | this.contextVariables.clear(); 36 | } 37 | 38 | protected MutableMap getContextVariables() 39 | { 40 | return this.contextVariables; 41 | } 42 | 43 | @Override 44 | public Value setVariable(String variableName, Value newValue) 45 | { 46 | if (this.hasVariable(variableName)) 47 | { 48 | exceptionByKey("DSL_VAR_IMMUTABLE").with("variableName", variableName).fire(); 49 | } 50 | 51 | this.getContextVariables().put(variableName, newValue); 52 | return newValue; 53 | } 54 | 55 | @Override 56 | public MapIterable getDeclaredFunctions() 57 | { 58 | return this.declaredFunctions; 59 | } 60 | 61 | @Override 62 | public void setDeclaredFunctions(MutableMap newDeclaredFunctions) 63 | { 64 | this.declaredFunctions = newDeclaredFunctions; 65 | } 66 | 67 | @Override 68 | public FunctionScript getDeclaredFunction(String functionName) 69 | { 70 | return this.declaredFunctions.get(functionName); 71 | } 72 | 73 | // helps avoid recursion 74 | public void loadFunctionsExcept(MapIterable functionsFromOutside, String nameToExclude) 75 | { 76 | functionsFromOutside.forEachKeyValue((k, v) -> { 77 | if (!k.equals(nameToExclude)) 78 | { 79 | this.declaredFunctions.put(k, v); 80 | } 81 | }); 82 | } 83 | 84 | @Override 85 | public String getString(String variableName) 86 | { 87 | return this.getVariable(variableName).stringValue(); 88 | } 89 | 90 | @Override 91 | public long getLong(String variableName) 92 | { 93 | return ((LongValue) this.getVariable(variableName)).longValue(); 94 | } 95 | 96 | @Override 97 | public LocalDate getDate(String variableName) 98 | { 99 | return ((DateValue) this.getVariable(variableName)).dateValue(); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/Expression.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public interface Expression 8 | { 9 | Value evaluate(ExpressionEvaluationVisitor evaluationVisitor); 10 | 11 | void accept(ExpressionVisitor visitor); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/FunctionCallExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | import org.eclipse.collections.impl.factory.Lists; 8 | 9 | public record FunctionCallExpr(String functionName, String normalizedFunctionName, ListIterable parameters) 10 | implements Expression 11 | { 12 | public FunctionCallExpr(String functionName, ListIterable parameters) 13 | { 14 | this(functionName, functionName.toUpperCase(), parameters); 15 | } 16 | 17 | public FunctionCallExpr(String newFunctionName) 18 | { 19 | this(newFunctionName, Lists.immutable.of()); 20 | } 21 | 22 | @Override 23 | public Value evaluate(ExpressionEvaluationVisitor visitor) 24 | { 25 | return visitor.visitFunctionCallExpr(this); 26 | } 27 | 28 | @Override 29 | public void accept(ExpressionVisitor visitor) 30 | { 31 | visitor.visitFunctionCallExpr(this); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/FunctionDescriptor.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import org.eclipse.collections.api.list.ListIterable; 4 | 5 | public interface FunctionDescriptor 6 | { 7 | String getName(); 8 | 9 | ListIterable getParameterNames(); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/FunctionScript.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | 8 | public class FunctionScript 9 | extends AbstractScript 10 | implements FunctionDescriptor 11 | { 12 | private final String name; 13 | private final String normalizedName; 14 | 15 | private final ListIterable parameterNames; 16 | 17 | public FunctionScript(String newName, ListIterable newParameterNames) 18 | { 19 | this.name = newName; 20 | this.normalizedName = this.name.toUpperCase(); 21 | this.parameterNames = newParameterNames; 22 | } 23 | 24 | @Override 25 | public String getName() 26 | { 27 | return this.name; 28 | } 29 | 30 | public String getNormalizedName() 31 | { 32 | return this.normalizedName; 33 | } 34 | 35 | @Override 36 | public void accept(ExpressionVisitor visitor) 37 | { 38 | visitor.visitFunctionScriptExpr(this); 39 | } 40 | 41 | @Override 42 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 43 | { 44 | return evaluationVisitor.visitFunctionScriptExpr(this); 45 | } 46 | 47 | @Override 48 | public ListIterable getParameterNames() 49 | { 50 | return this.parameterNames; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/IfElseExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record IfElseExpr(Expression condition, Expression ifScript, Expression elseScript, boolean isTernary) 8 | implements Expression 9 | { 10 | public IfElseExpr(Expression condition, Expression ifScript, Expression elseScript) 11 | { 12 | this(condition, ifScript, elseScript, false); 13 | } 14 | 15 | public IfElseExpr(Expression condition, Script ifScript) 16 | { 17 | this(condition, ifScript, null, false); 18 | } 19 | 20 | public boolean hasElseSection() 21 | { 22 | return this.elseScript != null; 23 | } 24 | 25 | @Override 26 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 27 | { 28 | return evaluationVisitor.visitIfElseExpr(this); 29 | } 30 | 31 | @Override 32 | public void accept(ExpressionVisitor visitor) 33 | { 34 | visitor.visitIfElseExpr(this); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/IndexExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record IndexExpr(Expression vectorExpr, Expression indexExpr) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 13 | { 14 | return evaluationVisitor.visitIndexExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitIndexExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/PredicateOp.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.BooleanValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import java.math.BigDecimal; 7 | import java.time.LocalDate; 8 | import java.time.LocalDateTime; 9 | 10 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 11 | 12 | public interface PredicateOp 13 | extends BinaryOp 14 | { 15 | @Override 16 | default BooleanValue apply(Value operand1, Value operand2) 17 | { 18 | if (operand1.isVoid() || operand2.isVoid()) 19 | { 20 | throw this.unsupportedOn("null values"); 21 | } 22 | 23 | return operand1.applyPredicate(operand2, this); 24 | } 25 | 26 | default BooleanValue applyString(String operand1, String operand2) 27 | { 28 | throw this.unsupportedOn("strings"); 29 | } 30 | 31 | default BooleanValue applyDecimal(BigDecimal operand1, BigDecimal operand2) 32 | { 33 | throw this.unsupportedOn("decimals"); 34 | } 35 | 36 | default BooleanValue applyDate(LocalDate operand1, LocalDate operand2) 37 | { 38 | throw this.unsupportedOn("dates"); 39 | } 40 | 41 | default BooleanValue applyDateTime(LocalDateTime operand1, LocalDateTime operand2) 42 | { 43 | throw this.unsupportedOn("datetime values"); 44 | } 45 | 46 | default BooleanValue applyLong(long operand1, long operand2) 47 | { 48 | throw this.unsupportedOn("longs"); 49 | } 50 | 51 | default BooleanValue applyDouble(double operand1, double operand2) 52 | { 53 | throw this.unsupportedOn("doubles"); 54 | } 55 | 56 | default RuntimeException unsupportedOn(String type) 57 | { 58 | return exceptionByKey("DSL_OP_NOT_SUPPORTED") 59 | .with("operation", this.asString()) 60 | .with("type", type) 61 | .getUnsupported(); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/ProjectionExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import io.github.vmzakharov.ecdataframe.dsl.visitor.PrettyPrintVisitor; 7 | import org.eclipse.collections.api.list.ListIterable; 8 | import org.eclipse.collections.api.list.MutableList; 9 | import org.eclipse.collections.impl.factory.Lists; 10 | 11 | public record ProjectionExpr( 12 | ListIterable projectionElements, 13 | Expression whereClause, 14 | ListIterable elementNames, 15 | ListIterable projectionExpressions) 16 | implements Expression 17 | { 18 | public ProjectionExpr(ListIterable newProjectionElements) 19 | { 20 | this(newProjectionElements, null); 21 | } 22 | 23 | public ProjectionExpr(ListIterable projectionElements, Expression whereClause) 24 | { 25 | this(projectionElements, whereClause, Lists.mutable.empty(), Lists.mutable.empty()); 26 | 27 | // need to update these properties but don't want to expose the mutable type in the record API 28 | // so the property is declared as "Iterable", which means we have to cast here to update it 29 | MutableList mElementNames = (MutableList) this.elementNames; 30 | MutableList mProjectionExpressions = (MutableList) this.projectionExpressions; 31 | 32 | for (int i = 0; i < this.projectionElements.size(); i++) 33 | { 34 | Expression element = this.projectionElements.get(i); 35 | if (element instanceof AliasExpr aliasExpr) 36 | { 37 | mElementNames.add(aliasExpr.alias()); 38 | mProjectionExpressions.add(aliasExpr.expression()); 39 | } 40 | else 41 | { 42 | mElementNames.add(PrettyPrintVisitor.exprToString(element)); 43 | mProjectionExpressions.add(element); 44 | } 45 | } 46 | } 47 | 48 | @Override 49 | public Value evaluate(ExpressionEvaluationVisitor visitor) 50 | { 51 | return visitor.visitProjectionExpr(this); 52 | } 53 | 54 | @Override 55 | public void accept(ExpressionVisitor visitor) 56 | { 57 | visitor.visitProjectionExpr(this); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/PropertyPathExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | 8 | public record PropertyPathExpr(ListIterable pathElements) 9 | implements Expression 10 | { 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor visitor) 13 | { 14 | return visitor.visitPropertyPathExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitPropertyPathExpr(this); 21 | } 22 | 23 | public String getEntityName() 24 | { 25 | return this.pathElements.getFirst(); 26 | } 27 | 28 | public String getPropertyChainString() 29 | { 30 | return this.pathElements.drop(1).makeString("."); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/Script.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import org.eclipse.collections.api.list.ListIterable; 4 | 5 | /** 6 | * A script is a sequence of one or more statements or expressions. The result of executing a script is the value of 7 | * the last statement (or the last standalone expression) that was executed in the script. 8 | */ 9 | public interface Script 10 | extends Expression 11 | { 12 | /** 13 | * adds an expression to the script 14 | * @param anExpression an expression to be added to the script 15 | * @return the expression 16 | */ 17 | Expression addExpression(Expression anExpression); 18 | 19 | /** 20 | * returns all the expressions from the script 21 | * @return a list of all the expressions in the script 22 | */ 23 | ListIterable getExpressions(); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/SimpleEvalContext.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataset.HierarchicalDataSet; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import org.eclipse.collections.api.RichIterable; 6 | import org.eclipse.collections.api.map.MutableMap; 7 | import org.eclipse.collections.impl.factory.Maps; 8 | 9 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 10 | 11 | public class SimpleEvalContext 12 | extends EvalContextAbstract 13 | { 14 | private final MutableMap dataSets = Maps.mutable.of(); 15 | 16 | @Override 17 | public Value getVariable(String newVariableName) 18 | { 19 | Value value = this.getContextVariables().get(newVariableName); 20 | 21 | if (value == null) 22 | { 23 | exceptionByKey("DSL_VAR_UNINITIALIZED").with("variableName", newVariableName).fire(); 24 | } 25 | 26 | return value; 27 | } 28 | 29 | @Override 30 | public Value getVariableOrDefault(String newVariableName, Value defaultValue) 31 | { 32 | Value value = this.getContextVariables().get(newVariableName); 33 | 34 | return (value == null) ? defaultValue : value; 35 | } 36 | 37 | @Override 38 | public void addDataSet(HierarchicalDataSet dataSet) 39 | { 40 | this.dataSets.put(dataSet.getName(), dataSet); 41 | } 42 | 43 | @Override 44 | public HierarchicalDataSet getDataSet(String dataSetName) 45 | { 46 | return this.dataSets.get(dataSetName); 47 | } 48 | 49 | @Override 50 | public RichIterable getVariableNames() 51 | { 52 | return this.getContextVariables().keysView(); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/StatementSequenceScript.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public class StatementSequenceScript 8 | extends AbstractScript 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 13 | { 14 | return evaluationVisitor.visitStatementSequenceScriptExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitStatementSequenceScript(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/UnaryExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record UnaryExpr(UnaryOp operation, Expression operand) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 13 | { 14 | return evaluationVisitor.visitUnaryExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitUnaryExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/VarExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | 7 | public record VarExpr(String variableName, boolean isEscaped) 8 | implements Expression 9 | { 10 | 11 | @Override 12 | public Value evaluate(ExpressionEvaluationVisitor visitor) 13 | { 14 | return visitor.visitVarExpr(this); 15 | } 16 | 17 | @Override 18 | public void accept(ExpressionVisitor visitor) 19 | { 20 | visitor.visitVarExpr(this); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/VectorExpr.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 4 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionEvaluationVisitor; 5 | import io.github.vmzakharov.ecdataframe.dsl.visitor.ExpressionVisitor; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | 8 | public record VectorExpr(ListIterable elements) 9 | implements Expression 10 | { 11 | 12 | @Override 13 | public Value evaluate(ExpressionEvaluationVisitor evaluationVisitor) 14 | { 15 | return evaluationVisitor.visitVectorExpr(this); 16 | } 17 | 18 | @Override 19 | public void accept(ExpressionVisitor visitor) 20 | { 21 | visitor.visitVectorExpr(this); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/function/IntrinsicFunctionDescriptorBuilder.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.function; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.EvalContext; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | import org.eclipse.collections.impl.factory.Lists; 8 | 9 | import java.util.function.Function; 10 | 11 | public class IntrinsicFunctionDescriptorBuilder 12 | extends IntrinsicFunctionDescriptor 13 | { 14 | private Function action = ignore -> Value.VOID; 15 | private Function, ValueType> returnTypeFunction = ignore -> ValueType.VOID; 16 | 17 | public IntrinsicFunctionDescriptorBuilder(String newName) 18 | { 19 | super(newName); 20 | } 21 | 22 | public IntrinsicFunctionDescriptorBuilder action(Function newAction) 23 | { 24 | this.action = newAction; 25 | return this; 26 | } 27 | 28 | public IntrinsicFunctionDescriptorBuilder parameterNames(ListIterable parameterNames) 29 | { 30 | this.setParameterNames(parameterNames); 31 | return this; 32 | } 33 | 34 | public IntrinsicFunctionDescriptorBuilder parameterNames(String... parameterNames) 35 | { 36 | return this.parameterNames(Lists.immutable.of(parameterNames)); 37 | } 38 | 39 | public IntrinsicFunctionDescriptorBuilder returnType(ValueType newReturnType) 40 | { 41 | this.returnType(ignore -> newReturnType); 42 | return this; 43 | } 44 | 45 | public IntrinsicFunctionDescriptorBuilder returnType(Function, ValueType> newReturnTypeFunction) 46 | { 47 | this.returnTypeFunction = newReturnTypeFunction; 48 | return this; 49 | } 50 | 51 | @Override 52 | public Value evaluate(EvalContext context) 53 | { 54 | return this.action.apply(context); 55 | } 56 | 57 | @Override 58 | public ValueType returnType(ListIterable parameterTypes) 59 | { 60 | return this.returnTypeFunction.apply(parameterTypes); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/DataFrameValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DataFrame; 4 | 5 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.exceptionByKey; 6 | 7 | public record DataFrameValue(DataFrame dataFrame) 8 | implements Value 9 | { 10 | public DataFrameValue 11 | { 12 | this.throwExceptionIfNull(dataFrame); 13 | } 14 | 15 | @Override 16 | public String asStringLiteral() 17 | { 18 | return "DataFrame [" + this.dataFrame.getName() + ", rows: " + this.dataFrame.rowCount() + ", columns: " + this.dataFrame.columnCount() + "] "; 19 | } 20 | 21 | @Override 22 | public ValueType getType() 23 | { 24 | return ValueType.DATA_FRAME; 25 | } 26 | 27 | @Override 28 | public String stringValue() 29 | { 30 | return this.dataFrame.asCsvString(); 31 | } 32 | 33 | @Override 34 | public int compareTo(Value o) 35 | { 36 | throw exceptionByKey("DSL_DF_COMPARE_UNSUPPORTED").getUnsupported(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/DateTimeValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 5 | 6 | import java.time.LocalDateTime; 7 | import java.time.format.DateTimeFormatter; 8 | 9 | public record DateTimeValue(LocalDateTime value) 10 | implements Value 11 | { 12 | static private final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE_TIME; 13 | 14 | public DateTimeValue 15 | { 16 | this.throwExceptionIfNull(value); 17 | } 18 | 19 | @Override 20 | public String asStringLiteral() 21 | { 22 | return this.value.format(this.getFormatter()); 23 | } 24 | 25 | @Override 26 | public Value apply(UnaryOp operation) 27 | { 28 | return operation.applyDateTime(this.value); 29 | } 30 | 31 | @Override 32 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 33 | { 34 | return operation.applyDateTime(this.dateTimeValue(), ((DateTimeValue) another).dateTimeValue()); 35 | } 36 | 37 | private DateTimeFormatter getFormatter() 38 | { 39 | return FORMATTER; 40 | } 41 | 42 | @Override 43 | public ValueType getType() 44 | { 45 | return ValueType.DATE_TIME; 46 | } 47 | 48 | public LocalDateTime dateTimeValue() 49 | { 50 | return this.value; 51 | } 52 | 53 | @Override 54 | public int compareTo(Value other) 55 | { 56 | this.checkSameTypeForComparison(other); 57 | return other.isVoid() ? 1 : this.dateTimeValue().compareTo(((DateTimeValue) other).dateTimeValue()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/DateValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 5 | 6 | import java.time.LocalDate; 7 | import java.time.format.DateTimeFormatter; 8 | 9 | public record DateValue(LocalDate value) 10 | implements Value 11 | { 12 | static private final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_DATE; 13 | 14 | public DateValue 15 | { 16 | this.throwExceptionIfNull(value); 17 | } 18 | 19 | @Override 20 | public String asStringLiteral() 21 | { 22 | return this.value.format(this.getFormatter()); 23 | } 24 | 25 | @Override 26 | public Value apply(UnaryOp operation) 27 | { 28 | return operation.applyDate(this.value); 29 | } 30 | 31 | @Override 32 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 33 | { 34 | return operation.applyDate(this.dateValue(), ((DateValue) another).dateValue()); 35 | } 36 | 37 | private DateTimeFormatter getFormatter() 38 | { 39 | return FORMATTER; 40 | } 41 | 42 | @Override 43 | public ValueType getType() 44 | { 45 | return ValueType.DATE; 46 | } 47 | 48 | public LocalDate dateValue() 49 | { 50 | return this.value; 51 | } 52 | 53 | @Override 54 | public int compareTo(Value other) 55 | { 56 | this.checkSameTypeForComparison(other); 57 | return other.isVoid() ? 1 : this.dateValue().compareTo(((DateValue) other).dateValue()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/DecimalValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | import java.math.BigDecimal; 8 | 9 | import static io.github.vmzakharov.ecdataframe.util.ExceptionFactory.*; 10 | 11 | public record DecimalValue(BigDecimal value) 12 | implements NumberValue 13 | { 14 | public DecimalValue 15 | { 16 | this.throwExceptionIfNull(value); 17 | } 18 | 19 | public DecimalValue(long unscaledValue, int scale) 20 | { 21 | this(BigDecimal.valueOf(unscaledValue, scale)); 22 | } 23 | 24 | @Override 25 | public String asStringLiteral() 26 | { 27 | return "[" + this.value.unscaledValue() + ',' + this.value.scale() + ']'; 28 | } 29 | 30 | @Override 31 | public Value apply(Value another, ArithmeticOp operation) 32 | { 33 | return operation.applyDecimal(this.decimalValue(), ((NumberValue) another).decimalValue()); 34 | } 35 | 36 | @Override 37 | public Value apply(UnaryOp operation) 38 | { 39 | return operation.applyDecimal(this.value); 40 | } 41 | 42 | @Override 43 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 44 | { 45 | return operation.applyDecimal(this.decimalValue(), ((DecimalValue) another).decimalValue()); 46 | } 47 | 48 | @Override 49 | public ValueType getType() 50 | { 51 | return ValueType.DECIMAL; 52 | } 53 | 54 | @Override 55 | public BigDecimal decimalValue() 56 | { 57 | return this.value; 58 | } 59 | 60 | @Override 61 | public double doubleValue() 62 | { 63 | throw exceptionByKey("DSL_NO_DEC_TO_FLOAT_CONVERSION").get(); 64 | } 65 | 66 | @Override 67 | public int compareTo(Value other) 68 | { 69 | this.checkSameTypeForComparison(other); 70 | 71 | return other.isVoid() ? 1 : this.decimalValue().compareTo(((DecimalValue) other).decimalValue()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/DoubleValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public record DoubleValue(double value) 10 | implements RealNumberValue 11 | { 12 | @Override 13 | public double doubleValue() 14 | { 15 | return this.value; 16 | } 17 | 18 | @Override 19 | public BigDecimal decimalValue() 20 | { 21 | return BigDecimal.valueOf(this.value); 22 | } 23 | 24 | @Override 25 | public String asStringLiteral() 26 | { 27 | return Double.toString(this.value); 28 | } 29 | 30 | @Override 31 | public Value apply(Value another, ArithmeticOp operation) 32 | { 33 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 34 | } 35 | 36 | @Override 37 | public Value apply(UnaryOp operation) 38 | { 39 | return operation.applyDouble(this.value); 40 | } 41 | 42 | @Override 43 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 44 | { 45 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 46 | } 47 | 48 | @Override 49 | public ValueType getType() 50 | { 51 | return ValueType.DOUBLE; 52 | } 53 | 54 | @Override 55 | public int compareTo(Value other) 56 | { 57 | this.checkSameTypeForComparison(other); 58 | return other.isVoid() ? 1 : Double.compare(this.doubleValue(), ((DoubleValue) other).doubleValue()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/FloatValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public record FloatValue(float value) 10 | implements RealNumberValue 11 | { 12 | public float floatValue() 13 | { 14 | return this.value; 15 | } 16 | 17 | @Override 18 | public double doubleValue() 19 | { 20 | return this.value; 21 | } 22 | 23 | @Override 24 | public BigDecimal decimalValue() 25 | { 26 | return BigDecimal.valueOf(this.value); 27 | } 28 | 29 | @Override 30 | public String asStringLiteral() 31 | { 32 | return Double.toString(this.value); 33 | } 34 | 35 | @Override 36 | public Value apply(Value another, ArithmeticOp operation) 37 | { 38 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 39 | } 40 | 41 | @Override 42 | public Value apply(UnaryOp operation) 43 | { 44 | return operation.applyFloat(this.value); 45 | } 46 | 47 | @Override 48 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 49 | { 50 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 51 | } 52 | 53 | @Override 54 | public ValueType getType() 55 | { 56 | return ValueType.FLOAT; 57 | } 58 | 59 | @Override 60 | public int compareTo(Value other) 61 | { 62 | this.checkSameTypeForComparison(other); 63 | return other.isVoid() ? 1 : Float.compare(this.floatValue(), ((FloatValue) other).floatValue()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/IntValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public record IntValue(int value) 10 | implements WholeNumberValue 11 | { 12 | @Override 13 | public String asStringLiteral() 14 | { 15 | return Integer.toString(this.value); 16 | } 17 | 18 | public int intValue() 19 | { 20 | return this.value; 21 | } 22 | 23 | @Override 24 | public double doubleValue() 25 | { 26 | return this.value; 27 | } 28 | 29 | @Override 30 | public long longValue() 31 | { 32 | return this.value; 33 | } 34 | 35 | @Override 36 | public BigDecimal decimalValue() 37 | { 38 | return BigDecimal.valueOf(this.value); 39 | } 40 | 41 | @Override 42 | public Value apply(Value another, ArithmeticOp operation) 43 | { 44 | if (another.isWholeNumber()) 45 | { 46 | return operation.applyLong(this.longValue(), ((WholeNumberValue) another).longValue()); 47 | } 48 | 49 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 50 | } 51 | 52 | @Override 53 | public Value apply(UnaryOp operation) 54 | { 55 | return operation.applyInt(this.value); 56 | } 57 | 58 | @Override 59 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 60 | { 61 | if (another.isWholeNumber()) 62 | { 63 | return operation.applyLong(this.longValue(), ((WholeNumberValue) another).longValue()); 64 | } 65 | 66 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 67 | } 68 | 69 | @Override 70 | public ValueType getType() 71 | { 72 | return ValueType.INT; 73 | } 74 | 75 | @Override 76 | public int compareTo(Value other) 77 | { 78 | this.checkSameTypeForComparison(other); 79 | return other.isVoid() ? 1 : Integer.compare(this.intValue(), ((IntValue) other).intValue()); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/LongValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | import java.math.BigDecimal; 8 | 9 | public record LongValue(long value) 10 | implements WholeNumberValue 11 | { 12 | @Override 13 | public String asStringLiteral() 14 | { 15 | return Long.toString(this.value); 16 | } 17 | 18 | @Override 19 | public long longValue() 20 | { 21 | return this.value; 22 | } 23 | 24 | @Override 25 | public double doubleValue() 26 | { 27 | return this.value; 28 | } 29 | 30 | @Override 31 | public BigDecimal decimalValue() 32 | { 33 | return BigDecimal.valueOf(this.value); 34 | } 35 | 36 | @Override 37 | public Value apply(Value another, ArithmeticOp operation) 38 | { 39 | if (another.isWholeNumber()) 40 | { 41 | return operation.applyLong(this.longValue(), ((WholeNumberValue) another).longValue()); 42 | } 43 | 44 | return operation.applyDouble(this.doubleValue(), ((NumberValue) another).doubleValue()); 45 | } 46 | 47 | @Override 48 | public Value apply(UnaryOp operation) 49 | { 50 | return operation.applyLong(this.value); 51 | } 52 | 53 | @Override 54 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 55 | { 56 | if (another.isDouble()) 57 | { 58 | return operation.applyDouble(this.doubleValue(), ((DoubleValue) another).doubleValue()); 59 | } 60 | return operation.applyLong(this.longValue(), ((LongValue) another).longValue()); 61 | } 62 | 63 | @Override 64 | public ValueType getType() 65 | { 66 | return ValueType.LONG; 67 | } 68 | 69 | @Override 70 | public int compareTo(Value other) 71 | { 72 | this.checkSameTypeForComparison(other); 73 | return other.isVoid() ? 1 : Long.compare(this.longValue(), ((LongValue) other).longValue()); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/NumberValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public interface NumberValue 6 | extends Value 7 | { 8 | double doubleValue(); 9 | 10 | BigDecimal decimalValue(); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/RealNumberValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | public interface RealNumberValue 4 | extends NumberValue 5 | { 6 | } 7 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/StringValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.PredicateOp; 5 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 6 | 7 | public record StringValue(String value) 8 | implements Value 9 | { 10 | public StringValue 11 | { 12 | this.throwExceptionIfNull(value); 13 | } 14 | 15 | @Override 16 | public Value apply(Value another, ArithmeticOp operation) 17 | { 18 | return operation.applyString(this.stringValue(), another.stringValue()); 19 | } 20 | 21 | @Override 22 | public Value apply(UnaryOp operation) 23 | { 24 | return operation.applyString(this.value); 25 | } 26 | 27 | @Override 28 | public String asStringLiteral() 29 | { 30 | return this.value.indexOf('\"') == -1 ? '"' + this.value + '"' : '\'' + this.value + '\''; 31 | } 32 | 33 | @Override 34 | public String stringValue() 35 | { 36 | return this.value; 37 | } 38 | 39 | @Override 40 | public BooleanValue applyPredicate(Value another, PredicateOp operation) 41 | { 42 | this.checkSameTypeForComparison(another); 43 | return operation.applyString(this.stringValue(), ((StringValue) another).stringValue()); 44 | } 45 | 46 | @Override 47 | public ValueType getType() 48 | { 49 | return ValueType.STRING; 50 | } 51 | 52 | @Override 53 | public int compareTo(Value other) 54 | { 55 | this.checkSameTypeForComparison(other); 56 | return other.isVoid() ? 1 : this.stringValue().compareTo(other.stringValue()); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/ValueType.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | public enum ValueType 4 | { 5 | BOOLEAN, DOUBLE, FLOAT, LONG, INT, DECIMAL, STRING, DATE, DATE_TIME, VOID, VECTOR, DATA_FRAME; 6 | 7 | public boolean isVoid() 8 | { 9 | return this == VOID; 10 | } 11 | 12 | public boolean isBoolean() 13 | { 14 | return this == BOOLEAN; 15 | } 16 | 17 | public boolean isLong() 18 | { 19 | return this == LONG; 20 | } 21 | 22 | public boolean isInt() 23 | { 24 | return this == INT; 25 | } 26 | 27 | public boolean isDouble() 28 | { 29 | return this == DOUBLE; 30 | } 31 | 32 | public boolean isFloat() 33 | { 34 | return this == FLOAT; 35 | } 36 | 37 | public boolean isDecimal() 38 | { 39 | return this == DECIMAL; 40 | } 41 | 42 | public boolean isNumber() 43 | { 44 | return this == LONG || this == DOUBLE || this == INT || this == FLOAT || this == DECIMAL; 45 | } 46 | 47 | public boolean isWholeNumber() 48 | { 49 | return this == LONG || this == INT; 50 | } 51 | 52 | public boolean isRealNumber() 53 | { 54 | return this == DOUBLE || this == FLOAT; 55 | } 56 | 57 | public boolean isDate() 58 | { 59 | return this == DATE; 60 | } 61 | 62 | public boolean isDateTime() 63 | { 64 | return this == DATE_TIME; 65 | } 66 | 67 | public boolean isTemporal() 68 | { 69 | return this == DATE || this == DATE_TIME; 70 | } 71 | 72 | public boolean isString() 73 | { 74 | return this == STRING; 75 | } 76 | 77 | public boolean isVector() 78 | { 79 | return this == VECTOR; 80 | } 81 | 82 | public boolean isDataFrame() 83 | { 84 | return this == DATA_FRAME; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/VectorValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.UnaryOp; 4 | import org.eclipse.collections.api.list.ListIterable; 5 | import org.eclipse.collections.impl.factory.Lists; 6 | 7 | public record VectorValue(ListIterable elements) 8 | implements Value 9 | { 10 | public static final VectorValue EMPTY = new VectorValue(Lists.immutable.empty()); 11 | 12 | @Override 13 | public String asStringLiteral() 14 | { 15 | return this.elements.makeString(Value::asStringLiteral, "(", ", ", ")"); 16 | } 17 | 18 | @Override 19 | public ValueType getType() 20 | { 21 | return ValueType.VECTOR; 22 | } 23 | 24 | public ListIterable getElements() 25 | { 26 | return this.elements; 27 | } 28 | 29 | public int size() 30 | { 31 | return this.elements.size(); 32 | } 33 | 34 | public Value get(int index) 35 | { 36 | return this.elements.get(index); 37 | } 38 | 39 | @Override 40 | public Value apply(UnaryOp operation) 41 | { 42 | return operation.applyVector(this.elements); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/value/WholeNumberValue.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | public interface WholeNumberValue 4 | extends NumberValue 5 | { 6 | long longValue(); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/visitor/ExpressionEvaluationVisitor.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.visitor; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.AnonymousScript; 4 | import io.github.vmzakharov.ecdataframe.dsl.AssignExpr; 5 | import io.github.vmzakharov.ecdataframe.dsl.BinaryExpr; 6 | import io.github.vmzakharov.ecdataframe.dsl.DecimalExpr; 7 | import io.github.vmzakharov.ecdataframe.dsl.FunctionCallExpr; 8 | import io.github.vmzakharov.ecdataframe.dsl.FunctionScript; 9 | import io.github.vmzakharov.ecdataframe.dsl.IfElseExpr; 10 | import io.github.vmzakharov.ecdataframe.dsl.IndexExpr; 11 | import io.github.vmzakharov.ecdataframe.dsl.ProjectionExpr; 12 | import io.github.vmzakharov.ecdataframe.dsl.PropertyPathExpr; 13 | import io.github.vmzakharov.ecdataframe.dsl.StatementSequenceScript; 14 | import io.github.vmzakharov.ecdataframe.dsl.UnaryExpr; 15 | import io.github.vmzakharov.ecdataframe.dsl.VarExpr; 16 | import io.github.vmzakharov.ecdataframe.dsl.VectorExpr; 17 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 18 | 19 | public interface ExpressionEvaluationVisitor 20 | { 21 | Value visitAssignExpr(AssignExpr expr); 22 | 23 | Value visitBinaryExpr(BinaryExpr expr); 24 | 25 | Value visitUnaryExpr(UnaryExpr expr); 26 | 27 | Value visitConstExpr(Value expr); 28 | 29 | Value visitFunctionCallExpr(FunctionCallExpr expr); 30 | 31 | Value visitIfElseExpr(IfElseExpr expr); 32 | 33 | Value visitPropertyPathExpr(PropertyPathExpr expr); 34 | 35 | Value visitAnonymousScriptExpr(AnonymousScript expr); 36 | 37 | Value visitFunctionScriptExpr(FunctionScript expr); 38 | 39 | Value visitStatementSequenceScriptExpr(StatementSequenceScript expr); 40 | 41 | Value visitVarExpr(VarExpr expr); 42 | 43 | Value visitProjectionExpr(ProjectionExpr expr); 44 | 45 | Value visitVectorExpr(VectorExpr expr); 46 | 47 | Value visitIndexExpr(IndexExpr expr); 48 | 49 | Value visitDecimalExpr(DecimalExpr expr); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/dsl/visitor/ExpressionVisitor.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.visitor; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.AliasExpr; 4 | import io.github.vmzakharov.ecdataframe.dsl.AnonymousScript; 5 | import io.github.vmzakharov.ecdataframe.dsl.AssignExpr; 6 | import io.github.vmzakharov.ecdataframe.dsl.BinaryExpr; 7 | import io.github.vmzakharov.ecdataframe.dsl.DecimalExpr; 8 | import io.github.vmzakharov.ecdataframe.dsl.FunctionCallExpr; 9 | import io.github.vmzakharov.ecdataframe.dsl.FunctionScript; 10 | import io.github.vmzakharov.ecdataframe.dsl.IfElseExpr; 11 | import io.github.vmzakharov.ecdataframe.dsl.IndexExpr; 12 | import io.github.vmzakharov.ecdataframe.dsl.ProjectionExpr; 13 | import io.github.vmzakharov.ecdataframe.dsl.PropertyPathExpr; 14 | import io.github.vmzakharov.ecdataframe.dsl.StatementSequenceScript; 15 | import io.github.vmzakharov.ecdataframe.dsl.UnaryExpr; 16 | import io.github.vmzakharov.ecdataframe.dsl.VarExpr; 17 | import io.github.vmzakharov.ecdataframe.dsl.VectorExpr; 18 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 19 | 20 | public interface ExpressionVisitor 21 | { 22 | void visitAliasExpr(AliasExpr expr); 23 | 24 | void visitAssignExpr(AssignExpr expr); 25 | 26 | void visitBinaryExpr(BinaryExpr expr); 27 | 28 | void visitUnaryExpr(UnaryExpr expr); 29 | 30 | void visitConstExpr(Value expr); 31 | 32 | void visitFunctionCallExpr(FunctionCallExpr expr); 33 | 34 | void visitIfElseExpr(IfElseExpr expr); 35 | 36 | void visitPropertyPathExpr(PropertyPathExpr expr); 37 | 38 | void visitAnonymousScriptExpr(AnonymousScript expr); 39 | 40 | void visitFunctionScriptExpr(FunctionScript expr); 41 | 42 | void visitStatementSequenceScript(StatementSequenceScript expr); 43 | 44 | void visitVarExpr(VarExpr expr); 45 | 46 | void visitProjectionExpr(ProjectionExpr expr); 47 | 48 | void visitVectorExpr(VectorExpr expr); 49 | 50 | void visitIndexExpr(IndexExpr expr); 51 | 52 | void visitDecimalExpr(DecimalExpr expr); 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/grammar/CollectingErrorListener.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.grammar; 2 | 3 | import org.antlr.v4.runtime.BaseErrorListener; 4 | import org.antlr.v4.runtime.RecognitionException; 5 | import org.antlr.v4.runtime.Recognizer; 6 | import org.antlr.v4.runtime.Token; 7 | import org.eclipse.collections.api.factory.Lists; 8 | import org.eclipse.collections.api.list.ListIterable; 9 | import org.eclipse.collections.api.list.MutableList; 10 | 11 | public class CollectingErrorListener 12 | extends BaseErrorListener 13 | { 14 | private final MutableList errors = Lists.mutable.of(); 15 | 16 | @Override 17 | public void syntaxError(Recognizer recognizer, 18 | Object offendingSymbol, 19 | int line, int charPositionInLine, 20 | String msg, 21 | RecognitionException e) 22 | { 23 | this.errors.add(new Error(offendingSymbol, line, charPositionInLine, msg)); 24 | } 25 | 26 | public ListIterable getErrors() 27 | { 28 | return this.errors; 29 | } 30 | 31 | public boolean hasErrors() 32 | { 33 | return this.errors.notEmpty(); 34 | } 35 | 36 | public static class Error 37 | { 38 | private final Object offendingSymbol; 39 | private final int line; 40 | private final int charPositionInLine; 41 | private final String message; 42 | 43 | public Error(Object newOffendingSymbol, int newLine, int newCharPositionInLine, String newMessage) 44 | { 45 | this.offendingSymbol = newOffendingSymbol; 46 | this.line = newLine; 47 | this.charPositionInLine = newCharPositionInLine; 48 | this.message = newMessage; 49 | } 50 | 51 | public int line() 52 | { 53 | return this.line; 54 | } 55 | 56 | public int charPositionInLine() 57 | { 58 | return this.charPositionInLine; 59 | } 60 | 61 | public int tokenStartIndex() 62 | { 63 | return ((Token) this.offendingSymbol).getStartIndex(); 64 | } 65 | 66 | public int tokenStopIndex() 67 | { 68 | return ((Token) this.offendingSymbol).getStopIndex(); 69 | } 70 | 71 | public String detailedErrorMessage() 72 | { 73 | return "line " + this.line + ":" + this.charPositionInLine + " at " + this.offendingSymbol + ": " + this.message; 74 | } 75 | 76 | public String briefErrorMessage() 77 | { 78 | return "line " + this.line + ":" + this.charPositionInLine + " " + this.message; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/CollectingPrinter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | public class CollectingPrinter 4 | implements Printer 5 | { 6 | private final StringBuilder buffer = new StringBuilder(); 7 | 8 | @Override 9 | public void print(String value) 10 | { 11 | this.buffer.append(value); 12 | } 13 | 14 | public String toString() 15 | { 16 | return this.buffer.toString(); 17 | } 18 | 19 | public void clear() 20 | { 21 | this.buffer.setLength(0); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/Printer.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | public interface Printer 4 | { 5 | void print(String value); 6 | 7 | default void newLine() 8 | { 9 | this.print("\n"); 10 | } 11 | 12 | default void println(String value) 13 | { 14 | this.print(value); 15 | this.newLine(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/PrinterFactory.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | final public class PrinterFactory 4 | { 5 | static private Printer printer = new SysOutPrinter(); 6 | static private Printer errPrinter = new SysErrPrinter(); 7 | 8 | private PrinterFactory() 9 | { 10 | // Utility class 11 | } 12 | 13 | public static Printer getPrinter() 14 | { 15 | return printer; 16 | } 17 | 18 | public static void setPrinter(Printer newPrinter) 19 | { 20 | printer = newPrinter; 21 | } 22 | 23 | public static Printer getErrPrinter() 24 | { 25 | return errPrinter; 26 | } 27 | 28 | public static void setErrPrinter(Printer newPrinter) 29 | { 30 | errPrinter = newPrinter; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/Stopwatch.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | public class Stopwatch 4 | { 5 | private long startTimeMillis; 6 | private long endTimeMillis; 7 | 8 | private long startUsedMemoryBytes; 9 | private long endUsedMemoryBytes; 10 | 11 | private long totalMemoryBytes; 12 | private long usedMemoryBytes; 13 | private long freeMemoryBytes; 14 | 15 | public void start() 16 | { 17 | this.recordMemoryUsage(); 18 | this.startUsedMemoryBytes = this.usedMemoryBytes; 19 | this.startTimeMillis = System.currentTimeMillis(); 20 | } 21 | 22 | public void stop() 23 | { 24 | this.endTimeMillis = System.currentTimeMillis(); 25 | this.recordMemoryUsage(); 26 | this.endUsedMemoryBytes = this.usedMemoryBytes; 27 | } 28 | 29 | public long elapsedTimeMillis() 30 | { 31 | return this.endTimeMillis - this.startTimeMillis; 32 | } 33 | 34 | public long usedMemoryChangeBytes() 35 | { 36 | return this.endUsedMemoryBytes - this.startUsedMemoryBytes; 37 | } 38 | 39 | public long totalMemoryBytes() 40 | { 41 | return this.totalMemoryBytes; 42 | } 43 | 44 | public long usedMemoryBytes() 45 | { 46 | return this.usedMemoryBytes; 47 | } 48 | 49 | public long freeMemoryBytes() 50 | { 51 | return this.freeMemoryBytes; 52 | } 53 | 54 | private void recordMemoryUsage() 55 | { 56 | System.gc(); 57 | System.gc(); 58 | System.gc(); 59 | Runtime runtime = Runtime.getRuntime(); 60 | 61 | this.freeMemoryBytes = runtime.freeMemory(); 62 | this.totalMemoryBytes = runtime.totalMemory(); 63 | this.usedMemoryBytes = this.totalMemoryBytes - this.freeMemoryBytes; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/SysErrPrinter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | public class SysErrPrinter 4 | implements Printer 5 | { 6 | @Override 7 | public void print(String value) 8 | { 9 | System.err.print(value); 10 | } 11 | 12 | @Override 13 | public void newLine() 14 | { 15 | System.err.println(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/io/github/vmzakharov/ecdataframe/util/SysOutPrinter.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.util; 2 | 3 | public class SysOutPrinter 4 | implements Printer 5 | { 6 | @Override 7 | public void print(String value) 8 | { 9 | System.out.print(value); 10 | } 11 | 12 | @Override 13 | public void newLine() 14 | { 15 | System.out.println(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/AggregateFunctionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.AggregateFunction; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 5 | import io.github.vmzakharov.ecdataframe.util.FormatWithPlaceholders; 6 | import org.eclipse.collections.api.list.ListIterable; 7 | import org.eclipse.collections.impl.factory.Lists; 8 | import org.junit.jupiter.api.Test; 9 | 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | public class AggregateFunctionTest 13 | { 14 | @Test 15 | public void testClone() 16 | { 17 | AggregateFunction sum = AggregateFunction.sum("Foo", "Bar"); 18 | AggregateFunction cloned = sum.cloneWith("Baz", "Qux"); 19 | 20 | assertEquals("Baz", cloned.getSourceColumnName()); 21 | assertEquals("Qux", cloned.getTargetColumnName()); 22 | assertEquals(sum.getClass(), cloned.getClass()); 23 | } 24 | 25 | @Test 26 | public void testFailureToClone() 27 | { 28 | AggregateFunction wontClone = new CloneResistant(); 29 | 30 | Exception exception = assertThrows( 31 | RuntimeException.class, 32 | () -> wontClone.cloneWith("Baz", "Qux") 33 | ); 34 | 35 | assertEquals( 36 | FormatWithPlaceholders 37 | .messageFromKey("AGG_CANNOT_CLONE") 38 | .with("operation", wontClone.getName()) 39 | .toString(), 40 | exception.getMessage() 41 | ); 42 | } 43 | 44 | private static class CloneResistant 45 | extends AggregateFunction 46 | { 47 | public CloneResistant() 48 | { 49 | super("ABC", "XYZ"); 50 | } 51 | 52 | @Override 53 | public ListIterable supportedSourceTypes() 54 | { 55 | return Lists.immutable.of(ValueType.STRING); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/DateExpressionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.time.LocalDate; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertEquals; 8 | import static org.junit.jupiter.api.Assertions.assertFalse; 9 | import static org.junit.jupiter.api.Assertions.assertTrue; 10 | 11 | public class DateExpressionTest 12 | { 13 | @Test 14 | public void parseDate() 15 | { 16 | LocalDate date = ExpressionTestUtil.evaluateToDate("toDate(\"2020-02-15\")"); 17 | assertEquals(LocalDate.of(2020, 2, 15), date); 18 | } 19 | 20 | @Test 21 | public void dateProximity() 22 | { 23 | assertTrue(ExpressionTestUtil.evaluateToBoolean("withinDays(toDate(\"2020-02-15\"), toDate(\"2020-02-17\"), 3)")); 24 | assertFalse(ExpressionTestUtil.evaluateToBoolean("withinDays(toDate(\"2020-02-11\"), toDate(\"2020-02-17\"), 3)")); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/FunctionDeclarationsTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | public class FunctionDeclarationsTest 11 | { 12 | @Test 13 | public void simpleFunction() 14 | { 15 | String scriptText = """ 16 | function x (a, b) 17 | { 18 | z = a - b 19 | a + b 20 | } 21 | 22 | y = 2 23 | x(5, y * 7)"""; 24 | 25 | Value result = ExpressionTestUtil.evaluateScript(scriptText); 26 | assertTrue(result.isLong()); 27 | assertEquals(19, ((LongValue) result).longValue()); 28 | } 29 | 30 | @Test 31 | public void manyFunctions() 32 | { 33 | String scriptText = """ 34 | function sum(a, b) 35 | { 36 | a + b 37 | } 38 | 39 | function mul(a, b) 40 | { 41 | a * b 42 | } 43 | 44 | one = 1 45 | two = 2 46 | sum( mul(5, two), SUM(one, two) )"""; 47 | 48 | Value result = ExpressionTestUtil.evaluateScript(scriptText); 49 | assertTrue(result.isLong()); 50 | assertEquals(13, ((LongValue) result).longValue()); 51 | } 52 | 53 | @Test 54 | public void noArgFunction() 55 | { 56 | String scriptText = """ 57 | function hello 58 | { 59 | "hello" 60 | } 61 | 62 | function bang 63 | { 64 | "!" 65 | } 66 | 67 | hello() + " there" + bang()"""; 68 | 69 | Value result = ExpressionTestUtil.evaluateScript(scriptText); 70 | assertTrue(result.isString()); 71 | assertEquals("hello there!", result.stringValue()); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/HandcraftedExpressionTreeTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.ArithmeticOp; 4 | import io.github.vmzakharov.ecdataframe.dsl.BinaryExpr; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.DoubleValue; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 7 | import io.github.vmzakharov.ecdataframe.dsl.value.StringValue; 8 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 9 | import io.github.vmzakharov.ecdataframe.dsl.visitor.InMemoryEvaluationVisitor; 10 | 11 | import org.junit.jupiter.api.BeforeEach; 12 | import org.junit.jupiter.api.Test; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | 16 | public class HandcraftedExpressionTreeTest 17 | { 18 | private InMemoryEvaluationVisitor evaluationVisitor; 19 | 20 | @BeforeEach 21 | public void setEvalContext() 22 | { 23 | this.evaluationVisitor = new InMemoryEvaluationVisitor(); 24 | } 25 | 26 | @Test 27 | public void intAddition() 28 | { 29 | Value result = new BinaryExpr( 30 | new LongValue(123), 31 | new LongValue(456), 32 | ArithmeticOp.ADD 33 | ).evaluate(this.evaluationVisitor); 34 | 35 | assertEquals(579, ((LongValue) result).longValue()); 36 | } 37 | 38 | @Test 39 | public void doubleAddition() 40 | { 41 | Value result = new BinaryExpr( 42 | new DoubleValue(123.321), 43 | new DoubleValue(456.025), 44 | ArithmeticOp.ADD 45 | ).evaluate(this.evaluationVisitor); 46 | 47 | assertEquals(579.346, ((DoubleValue) result).doubleValue(), 0.0000000001); 48 | } 49 | 50 | @Test 51 | public void stringAddition() 52 | { 53 | Value result = new BinaryExpr( 54 | new StringValue("Oompa"), 55 | new StringValue("Loompa"), 56 | ArithmeticOp.ADD 57 | ).evaluate(this.evaluationVisitor); 58 | 59 | assertEquals("OompaLoompa", result.stringValue()); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/ImmutableVariablesTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.util.FormatWithPlaceholders; 4 | import org.junit.jupiter.api.Assertions; 5 | import org.junit.jupiter.api.Test; 6 | 7 | import static org.junit.jupiter.api.Assertions.assertThrows; 8 | 9 | public class ImmutableVariablesTest 10 | { 11 | @Test 12 | public void immutableVariables() 13 | { 14 | Exception exception = assertThrows( 15 | RuntimeException.class, 16 | () -> ExpressionTestUtil.evaluateScript( 17 | "a = 1\n" 18 | + "a = 2") 19 | ); 20 | 21 | Assertions.assertEquals( 22 | FormatWithPlaceholders.messageFromKey("DSL_VAR_IMMUTABLE").with("variableName", "a").toString(), 23 | exception.getMessage() 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/IsEmptyOperationTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.SimpleEvalContext; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 5 | 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static io.github.vmzakharov.ecdataframe.ExpressionTestUtil.scriptEvaluatesToFalse; 9 | import static io.github.vmzakharov.ecdataframe.ExpressionTestUtil.scriptEvaluatesToTrue; 10 | import static org.junit.jupiter.api.Assertions.*; 11 | 12 | public class IsEmptyOperationTest 13 | { 14 | @Test 15 | public void stringIsEmptyOrNot() 16 | { 17 | scriptEvaluatesToFalse("\"Foo\" is empty"); 18 | scriptEvaluatesToFalse("\"Foo\" is empty or \"Bar\" is empty"); 19 | 20 | scriptEvaluatesToTrue("\"Foo\" is not empty"); 21 | scriptEvaluatesToTrue("\"Foo\" is not empty and 2 > 1"); 22 | 23 | scriptEvaluatesToTrue("\"\" is empty"); 24 | scriptEvaluatesToFalse("\"\" is not empty"); 25 | 26 | scriptEvaluatesToTrue("\"Foo\" is not empty and \"\" is empty"); 27 | } 28 | 29 | @Test 30 | public void dateIsEmptyOrNot() 31 | { 32 | scriptEvaluatesToFalse("toDate(\"2020-10-06\") is empty"); 33 | scriptEvaluatesToFalse("toDate(\"2020-10-06\") is empty or \"Bar\" is empty"); 34 | 35 | scriptEvaluatesToTrue("toDate(\"2020-10-06\") is not empty"); 36 | } 37 | 38 | @Test 39 | public void vectorIsEmptyOrNot() 40 | { 41 | scriptEvaluatesToFalse("(1, 2, 3) is empty"); 42 | scriptEvaluatesToFalse("(1, 2, 3) is empty or \"Bar\" is empty"); 43 | 44 | scriptEvaluatesToTrue("(1, 2, 3) is not empty"); 45 | 46 | scriptEvaluatesToTrue("() is empty"); 47 | scriptEvaluatesToFalse("() is not empty"); 48 | } 49 | 50 | @Test 51 | public void voidValueIsNull() 52 | { 53 | SimpleEvalContext context = new SimpleEvalContext(); 54 | context.setVariable("x", Value.VOID); 55 | 56 | scriptEvaluatesToTrue("x is null", context); 57 | scriptEvaluatesToFalse("x is not null", context); 58 | } 59 | 60 | @Test 61 | public void voidValueNotIsNotEmpty() 62 | { 63 | SimpleEvalContext context = new SimpleEvalContext(); 64 | context.setVariable("x", Value.VOID); 65 | 66 | assertThrows( 67 | UnsupportedOperationException.class, 68 | () -> scriptEvaluatesToFalse("x is not empty", context) 69 | ); 70 | } 71 | 72 | @Test 73 | public void voidValueNotIsEmpty() 74 | { 75 | SimpleEvalContext context = new SimpleEvalContext(); 76 | context.setVariable("x", Value.VOID); 77 | 78 | assertThrows( 79 | UnsupportedOperationException.class, 80 | () -> scriptEvaluatesToFalse("x is empty", context) 81 | ); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/TypeInferenceForBuiltInFunctionsTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.EvalContext; 4 | import io.github.vmzakharov.ecdataframe.dsl.function.BuiltInFunctions; 5 | import io.github.vmzakharov.ecdataframe.dsl.function.IntrinsicFunctionDescriptor; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 7 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 8 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 9 | import org.eclipse.collections.api.list.ListIterable; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import static io.github.vmzakharov.ecdataframe.TypeInferenceUtil.assertScriptType; 13 | 14 | public class TypeInferenceForBuiltInFunctionsTest 15 | { 16 | @Test 17 | public void abs() 18 | { 19 | assertScriptType("abs(5)", ValueType.LONG); 20 | assertScriptType("abs(1.23)", ValueType.DOUBLE); 21 | } 22 | 23 | @Test 24 | public void startswith() 25 | { 26 | assertScriptType("startsWith('ABCD', 'AB')", ValueType.BOOLEAN); 27 | } 28 | 29 | @Test 30 | public void contains() 31 | { 32 | assertScriptType("contains('ABCD', 'AB')", ValueType.BOOLEAN); 33 | } 34 | 35 | @Test 36 | public void toUpper() 37 | { 38 | assertScriptType("toUpper('ABCD')", ValueType.STRING); 39 | } 40 | 41 | @Test 42 | public void substr() 43 | { 44 | assertScriptType("substr('ABCD', 2)", ValueType.STRING); 45 | assertScriptType("substr('ABCD', 1, 2)", ValueType.STRING); 46 | } 47 | 48 | @Test 49 | public void toStringFunction() 50 | { 51 | assertScriptType("toString('ABCD')", ValueType.STRING); 52 | assertScriptType("toString(123)", ValueType.STRING); 53 | assertScriptType("toString(12.34)", ValueType.STRING); 54 | assertScriptType("toString(toDate(2020, 10, 25))", ValueType.STRING); 55 | } 56 | 57 | @Test 58 | public void toLong() 59 | { 60 | assertScriptType("toLong('123')", ValueType.LONG); 61 | } 62 | 63 | @Test 64 | public void toDobule() 65 | { 66 | assertScriptType("toDouble('456')", ValueType.DOUBLE); 67 | } 68 | 69 | @Test 70 | public void toDate() 71 | { 72 | assertScriptType("toDate(2001, 11, 22)", ValueType.DATE); 73 | } 74 | 75 | @Test 76 | public void toDateTime() 77 | { 78 | assertScriptType("toDateTime(2001, 11, 22, 10, 10, 10)", ValueType.DATE_TIME); 79 | } 80 | 81 | @Test 82 | public void functionAddedAtRuntime() 83 | { 84 | BuiltInFunctions.addFunctionDescriptor(new IntrinsicFunctionDescriptor("two") 85 | { 86 | @Override 87 | public Value evaluate(EvalContext context) 88 | { 89 | return new LongValue(2); 90 | } 91 | 92 | @Override 93 | public ValueType returnType(ListIterable parameterTypes) 94 | { 95 | return ValueType.LONG; 96 | } 97 | }); 98 | 99 | assertScriptType("two()", ValueType.LONG); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/TypeInferenceUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.EvalContext; 4 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 5 | import io.github.vmzakharov.ecdataframe.dsl.SimpleEvalContext; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 7 | import io.github.vmzakharov.ecdataframe.dsl.visitor.TypeInferenceVisitor; 8 | import org.junit.jupiter.api.Assertions; 9 | 10 | final public class TypeInferenceUtil 11 | { 12 | private TypeInferenceUtil() 13 | { 14 | // utility class 15 | } 16 | 17 | public static void assertScriptType(String scriptAsString, ValueType valueType) 18 | { 19 | assertScriptType(scriptAsString, new SimpleEvalContext(), valueType); 20 | } 21 | 22 | public static void assertScriptType(String scriptAsString, EvalContext context, ValueType valueType) 23 | { 24 | Expression expression = ExpressionTestUtil.toScript(scriptAsString); 25 | TypeInferenceVisitor visitor = new TypeInferenceVisitor(context); 26 | expression.accept(visitor); 27 | Assertions.assertEquals(valueType, visitor.getLastExpressionType()); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/VectorExpressionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.BooleanValue; 4 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 6 | import org.junit.jupiter.api.Test; 7 | 8 | import static org.junit.jupiter.api.Assertions.*; 9 | 10 | public class VectorExpressionTest 11 | { 12 | @Test 13 | public void vectorVariableIn() 14 | { 15 | Value result = ExpressionTestUtil.evaluateScript( 16 | """ 17 | x = ("a", "b", "c") 18 | y = "a" 19 | y in x 20 | """ 21 | ); 22 | 23 | assertTrue(((BooleanValue) result).isTrue()); 24 | } 25 | 26 | @Test 27 | public void index() 28 | { 29 | assertEquals(2L, ExpressionTestUtil.evaluateToLong("(1, 2, 3)[1]")); 30 | } 31 | 32 | @Test 33 | public void indexWithVariables() 34 | { 35 | Value result = ExpressionTestUtil.evaluateScript( 36 | """ 37 | x = ("a", "b", "c") 38 | y = 2 39 | x[y] 40 | """ 41 | ); 42 | 43 | assertEquals("c", result.stringValue()); 44 | } 45 | 46 | @Test 47 | public void indexWithFunction() 48 | { 49 | Value result = ExpressionTestUtil.evaluateScript( 50 | """ 51 | function foo(switch) 52 | { 53 | if switch == 1 then 54 | (1, 2, 3) 55 | else 56 | (4, 5, 6) 57 | endif 58 | } 59 | 60 | foo(2)[2] + foo(1)[1]\s""" 61 | ); 62 | 63 | assertEquals(8L, ((LongValue) result).longValue()); 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataframe/ColumnIteratorTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import org.junit.jupiter.api.BeforeEach; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import static org.junit.jupiter.api.Assertions.*; 7 | 8 | public class ColumnIteratorTest 9 | { 10 | private DataFrame dataFrame; 11 | 12 | @BeforeEach 13 | public void setupDataFrame() 14 | { 15 | this.dataFrame = new DataFrame("Test") 16 | .addStringColumn("Name").addStringColumn("Quux").addStringColumn("Waldo") 17 | .addRow("Alice", "A", "A") 18 | .addRow("Bob", "B", "A") 19 | .addRow("Carl", "C", null) 20 | .addRow("Doris", "D", "B") 21 | ; 22 | } 23 | 24 | @Test 25 | public void injectInto() 26 | { 27 | int totalStringLength = this.dataFrame.getStringColumn("Name") 28 | .injectIntoBreakOnNulls( 29 | 0, 30 | (len, s) -> len + s.length() 31 | ); 32 | 33 | assertEquals(17, totalStringLength); 34 | } 35 | 36 | @Test 37 | public void injectIntoWithBreakOnNullResult() 38 | { 39 | Integer totalStringLength = this.dataFrame.getStringColumn("Quux") 40 | .injectIntoBreakOnNulls( 41 | 0, 42 | (len, s) -> { 43 | if (s.equals("C")) 44 | { 45 | fail("Shouldn't reach C"); 46 | } 47 | 48 | return s.equals("B") ? null : len + s.length(); 49 | } 50 | ); 51 | 52 | assertNull(totalStringLength); 53 | } 54 | 55 | @Test 56 | public void injectIntoWithBreakOnNullValue() 57 | { 58 | Integer totalStringLength = this.dataFrame.getStringColumn("Waldo") 59 | .injectIntoBreakOnNulls( 60 | 0, 61 | (len, s) -> { 62 | if (!s.equals("A")) 63 | { 64 | fail("Shouldn't get here"); 65 | } 66 | 67 | return len + s.length(); 68 | } 69 | ); 70 | 71 | assertNull(totalStringLength); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataframe/DataFrameAggregationErrorMessageTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 4 | import io.github.vmzakharov.ecdataframe.util.FormatWithPlaceholders; 5 | import org.eclipse.collections.api.list.ListIterable; 6 | import org.eclipse.collections.impl.factory.Lists; 7 | 8 | import org.junit.jupiter.api.Assertions; 9 | import org.junit.jupiter.api.BeforeEach; 10 | import org.junit.jupiter.api.Test; 11 | 12 | import java.time.LocalDate; 13 | 14 | import static org.junit.jupiter.api.Assertions.*; 15 | 16 | public class DataFrameAggregationErrorMessageTest 17 | { 18 | private DataFrame dataFrame; 19 | 20 | @BeforeEach 21 | public void initialiseDataFrame() 22 | { 23 | this.dataFrame = new DataFrame("FrameOfData") 24 | .addStringColumn("Name").addStringColumn("Foo").addLongColumn("Bar").addDoubleColumn("Baz") 25 | .addIntColumn("Waldo").addDateColumn("Corge") 26 | .addRow("Alice", "Abc", 123L, 10.0, 100, LocalDate.of(2023, 10, 21)) 27 | .addRow("Bob", "Def", 456L, 12.0, -25, LocalDate.of(2024, 11, 22)) 28 | .addRow("Carol", "Xyz", -789L, 17.0, 42, LocalDate.of(2025, 12, 23)); 29 | } 30 | 31 | @Test 32 | public void aggString() 33 | { 34 | this.checkAggMessage("Name", "string"); 35 | } 36 | 37 | @Test 38 | public void aggLong() 39 | { 40 | this.checkAggMessage("Bar", "long"); 41 | } 42 | 43 | @Test 44 | public void aggDouble() 45 | { 46 | this.checkAggMessage("Baz", "double"); 47 | } 48 | 49 | @Test 50 | public void aggInt() 51 | { 52 | this.checkAggMessage("Waldo", "int"); 53 | } 54 | 55 | @Test 56 | public void aggDate() 57 | { 58 | this.checkAggMessage("Corge", "date"); 59 | } 60 | 61 | public void checkAggMessage(String columnName, String columnType) 62 | { 63 | AggregateFunction aggregator = new BadAggregation(columnName); 64 | 65 | Exception e = Assertions.assertThrows(UnsupportedOperationException.class, () -> this.dataFrame.aggregate(Lists.immutable.of(aggregator))); 66 | 67 | assertEquals( 68 | FormatWithPlaceholders.messageFromKey("AGG_COL_TYPE_UNSUPPORTED") 69 | .with("operation", aggregator.getName()) 70 | .with("operationDescription", aggregator.getDescription()) 71 | .with("columnName", columnName) 72 | .with("columnType", columnType) 73 | .toString(), 74 | e.getMessage()); 75 | } 76 | 77 | private static class BadAggregation 78 | extends AggregateFunction 79 | { 80 | public BadAggregation(String newColumnName) 81 | { 82 | super(newColumnName); 83 | } 84 | 85 | @Override 86 | public ListIterable supportedSourceTypes() 87 | { 88 | return Lists.immutable.of(ValueType.STRING, ValueType.LONG); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataframe/DataFrameExpressionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.ExpressionTestUtil; 4 | import io.github.vmzakharov.ecdataframe.dsl.Expression; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 6 | import org.eclipse.collections.api.factory.Lists; 7 | import org.eclipse.collections.api.list.ImmutableList; 8 | import org.eclipse.collections.api.list.primitive.MutableLongList; 9 | import org.eclipse.collections.impl.factory.primitive.LongLists; 10 | import org.eclipse.collections.impl.list.primitive.IntInterval; 11 | 12 | import org.junit.jupiter.api.BeforeEach; 13 | import org.junit.jupiter.api.Test; 14 | 15 | import static org.junit.jupiter.api.Assertions.*; 16 | 17 | public class DataFrameExpressionTest 18 | { 19 | private DataFrame df; 20 | 21 | @BeforeEach 22 | public void initializeDataFrame() 23 | { 24 | this.df = new DataFrame("this.df1") 25 | .addStringColumn("Name").addLongColumn("Count").addDoubleColumn("Value"); 26 | 27 | this.df 28 | .addRow("Alice", 5, 23.45) 29 | .addRow("Bob", 10, 12.34) 30 | .addRow("Carol", 11, 56.78) 31 | .addRow("Dan", 0, 7.89); 32 | } 33 | 34 | @Test 35 | public void arithmeticExpression() 36 | { 37 | Expression expression = ExpressionTestUtil.toExpression("Count * 2"); 38 | 39 | MutableLongList values = LongLists.mutable.of(); 40 | 41 | IntInterval.zeroTo(3) 42 | .collectLong(i -> { 43 | this.df.setEvalContextRowIndex(i); 44 | return ((LongValue) expression.evaluate(this.df.getEvalVisitor())).longValue(); 45 | } 46 | , values 47 | ); 48 | 49 | assertEquals(LongLists.immutable.of(10, 20, 22, 0), values); 50 | } 51 | 52 | @Test 53 | public void stringExpression() 54 | { 55 | Expression expression = ExpressionTestUtil.toExpression("substr(Name, 2) + \"X\""); 56 | 57 | ImmutableList values = IntInterval.zeroTo(3).collect(i -> { 58 | this.df.setEvalContextRowIndex(i); 59 | return expression.evaluate(this.df.getEvalVisitor()).stringValue(); 60 | } 61 | ); 62 | 63 | assertEquals(Lists.immutable.of("iceX", "bX", "rolX", "nX"), values); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataframe/DataFrameUtil.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.util.DataFrameCompare; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | final public class DataFrameUtil 8 | { 9 | private DataFrameUtil() 10 | { 11 | // Utility class 12 | } 13 | 14 | static public void assertEquals(DataFrame expected, DataFrame actual) 15 | { 16 | DataFrameCompare comparer = new DataFrameCompare(); 17 | 18 | if (!comparer.equal(expected, actual)) 19 | { 20 | fail(comparer.reason()); 21 | } 22 | } 23 | 24 | static public void assertEquals(DataFrame expected, DataFrame actual, double tolerance) 25 | { 26 | DataFrameCompare comparer = new DataFrameCompare(); 27 | 28 | if (!comparer.equal(expected, actual, tolerance)) 29 | { 30 | fail(comparer.reason()); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataframe/TupleCompareTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataframe; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.util.Arrays; 6 | 7 | import static org.junit.jupiter.api.Assertions.*; 8 | 9 | public class TupleCompareTest 10 | { 11 | @Test 12 | public void compare() 13 | { 14 | DfTuple t1 = new DfTuple(0, "B", null); 15 | DfTuple t2 = new DfTuple(0, null, null); 16 | DfTuple t3 = new DfTuple(0, "A", null); 17 | DfTuple t4 = new DfTuple(0, null, 2L); 18 | DfTuple t5 = new DfTuple(0, "A", 2L); 19 | DfTuple t6 = new DfTuple(0, null, null); 20 | 21 | assertTrue(t1.compareTo(t2) > 0); 22 | assertTrue(t2.compareTo(t1) < 0); 23 | assertNotEquals(t1, t2); 24 | 25 | assertTrue(t5.compareTo(t3) > 0); 26 | assertTrue(t3.compareTo(t5) < 0); 27 | assertNotEquals(t5, t3); 28 | 29 | assertTrue(t5.compareTo(t4) > 0); 30 | assertTrue(t4.compareTo(t5) < 0); 31 | assertNotEquals(t5, t3); 32 | 33 | assertEquals(0, t2.compareTo(t6)); 34 | assertEquals(t2, t6); 35 | 36 | assertEquals(0, t4.compareTo(t4)); 37 | assertEquals(0, t5.compareTo(t5)); 38 | assertEquals(0, t6.compareTo(t6)); 39 | } 40 | 41 | @Test 42 | public void sort() 43 | { 44 | DfTuple t2 = new DfTuple(0, null, null); 45 | DfTuple t6 = new DfTuple(0, null, null); 46 | DfTuple t4 = new DfTuple(0, null, 2L); 47 | DfTuple t3 = new DfTuple(0, "A", null); 48 | DfTuple t5 = new DfTuple(0, "A", 2L); 49 | DfTuple t1 = new DfTuple(0, "B", null); 50 | 51 | DfTuple[] tuples = new DfTuple[] {t1, t2, t3, t4, t5, t6}; 52 | 53 | Arrays.sort(tuples); 54 | 55 | assertArrayEquals(new DfTuple[] {t2, t6, t4, t3, t5, t1}, tuples); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataset/CsvCodeScratchpad.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import io.github.vmzakharov.ecdataframe.dataframe.DataFrame; 4 | import io.github.vmzakharov.ecdataframe.util.Stopwatch; 5 | 6 | import java.nio.file.Paths; 7 | 8 | public class CsvCodeScratchpad 9 | { 10 | public static void main(String[] args) 11 | { 12 | System.out.println("Current working directory: " + System.getProperty("user.dir")); 13 | 14 | CsvCodeScratchpad csvCodeScratchpad = new CsvCodeScratchpad(); 15 | 16 | csvCodeScratchpad.readStuff(); 17 | } 18 | 19 | private void readStuff() 20 | { 21 | String fileName = "src/test/resources/employees.csv"; 22 | String fileNameOut = "src/test/resources/employees_out.csv"; 23 | 24 | CsvDataSet dataSet = new CsvDataSet(Paths.get(fileName), "Employee"); 25 | dataSet.convertEmptyElementsToNulls(); 26 | 27 | Stopwatch stopwatch = new Stopwatch(); 28 | stopwatch.start(); 29 | stopwatch.stop(); 30 | 31 | stopwatch.start(); 32 | DataFrame dataFrame1 = dataSet.loadAsDataFrame(); 33 | stopwatch.stop(); 34 | this.report("Loaded", dataFrame1.rowCount(), stopwatch); 35 | 36 | stopwatch.start(); 37 | CsvDataSet dataSetOut = new CsvDataSet(Paths.get(fileNameOut), "Employee"); 38 | dataSetOut.write(dataFrame1); 39 | stopwatch.stop(); 40 | this.report("Wrote", dataFrame1.rowCount(), stopwatch); 41 | } 42 | 43 | private void report(String message, int rowCount, Stopwatch stopwatch) 44 | { 45 | System.out.printf(message + ": %,d rows\n", rowCount); 46 | System.out.printf("Elapsed time, ms: %,d, Memory used, bytes: %,d [T: %,d, F: %,d, U: %,d]\n", 47 | stopwatch.elapsedTimeMillis(), 48 | stopwatch.usedMemoryChangeBytes(), 49 | stopwatch.totalMemoryBytes(), 50 | stopwatch.freeMemoryBytes(), 51 | stopwatch.usedMemoryBytes() 52 | ); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataset/SeparatedTextSplittingTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import org.eclipse.collections.api.factory.Lists; 4 | import org.eclipse.collections.api.list.MutableList; 5 | import org.junit.jupiter.api.Assertions; 6 | import org.junit.jupiter.api.BeforeEach; 7 | import org.junit.jupiter.api.Test; 8 | 9 | public class SeparatedTextSplittingTest 10 | { 11 | private CsvDataSet dataSet; 12 | 13 | @BeforeEach 14 | public void configureDataSet() 15 | { 16 | this.dataSet = new CsvDataSet("Foo", "Bar", new CsvSchema()); 17 | } 18 | 19 | @Test 20 | public void simpleString() 21 | { 22 | this.validateSplitting("a,b,c", new String[] {"a", "b", "c"}); 23 | } 24 | 25 | @Test 26 | public void splitStringWithSpacesAndEmptyValues() 27 | { 28 | this.validateSplitting( 29 | "eenie, meenie , , miny,, moe", 30 | new String[] {"eenie", " meenie ", " ", " miny", "", " moe"}); 31 | } 32 | 33 | @Test 34 | public void splitStringWithNulls() 35 | { 36 | this.dataSet.convertEmptyElementsToNulls(); 37 | this.validateSplitting( 38 | "eenie,, miny, , moe,", 39 | new String[] {"eenie", null, " miny", " ", " moe", null}); 40 | } 41 | 42 | @Test 43 | public void splitStringWithFirstAndLastEmptyValues() 44 | { 45 | this.validateSplitting( 46 | ",eenie, meenie , , miny,, moe,", 47 | new String[] {"", "eenie", " meenie ", " ", " miny", "", " moe", ""}); 48 | } 49 | 50 | @Test 51 | public void stringWithFirstAndLastBlankValues() 52 | { 53 | this.validateSplitting(" ,eenie, meenie, ", new String[] {" ", "eenie", " meenie", " "}); 54 | } 55 | 56 | @Test 57 | public void stringWithQuotesAndFirstAndLastBlankValues() 58 | { 59 | this.validateSplitting(" ,\"eenie\",\" meenie\", ", new String[] {" ", "\"eenie\"", "\" meenie\"", " "}); 60 | } 61 | 62 | @Test 63 | public void simpleStringWithQuotes() 64 | { 65 | this.validateSplitting("\"aa\",\"bb\",\"cc\"", new String[] {"\"aa\"", "\"bb\"", "\"cc\""}); 66 | } 67 | 68 | @Test 69 | public void simpleStringWithQuotesAndSpacesInVariousPlaces() 70 | { 71 | this.validateSplitting( 72 | " \"aa\", \"bb\", \"\",\" c c \",", 73 | new String[] {"\"aa\"", "\"bb\"", "\"\"", "\" c c \"", ""}); 74 | } 75 | 76 | @Test 77 | public void mixedQuotedTokensAndNot() 78 | { 79 | this.validateSplitting( 80 | " \"aa\", bb, \"\",\" c c \", dd ", 81 | new String[] {"\"aa\"", " bb", "\"\"", "\" c c \"", " dd "}); 82 | } 83 | 84 | private void validateSplitting(String input, String[] expected) 85 | { 86 | MutableList result = Lists.mutable.of(); 87 | this.dataSet.splitMindingQsInto(input, result); 88 | 89 | Assertions.assertArrayEquals(expected, result.toArray()); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dataset/StringBasedCsvDataSet.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dataset; 2 | 3 | import java.io.Reader; 4 | import java.io.StringReader; 5 | import java.io.StringWriter; 6 | import java.io.Writer; 7 | 8 | public class StringBasedCsvDataSet 9 | extends CsvDataSet 10 | { 11 | private final String data; 12 | private final StringWriter writer = new StringWriter(); 13 | 14 | public StringBasedCsvDataSet(String dataFileName, String newName, String newData) 15 | { 16 | super(dataFileName, newName); 17 | this.data = newData; 18 | } 19 | 20 | public StringBasedCsvDataSet(String dataFileName, String newName, CsvSchema newSchema, String newData) 21 | { 22 | super(dataFileName, newName, newSchema); 23 | this.data = newData; 24 | } 25 | 26 | @Override 27 | protected Reader createReader() 28 | { 29 | return new StringReader(this.data); 30 | } 31 | 32 | @Override 33 | protected Writer createWriter() 34 | { 35 | this.writer.getBuffer().setLength(0); // to mimic the behavior of the superclass 36 | return this.writer; 37 | } 38 | 39 | public String getWrittenData() 40 | { 41 | return this.writer.toString(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dsl/DecimalExpressionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl; 2 | 3 | import io.github.vmzakharov.ecdataframe.dsl.value.DecimalValue; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.math.BigDecimal; 7 | import java.math.MathContext; 8 | 9 | import static io.github.vmzakharov.ecdataframe.ExpressionTestUtil.evaluateScriptWithContext; 10 | import static io.github.vmzakharov.ecdataframe.ExpressionTestUtil.evaluateToDecimal; 11 | import static org.junit.jupiter.api.Assertions.*; 12 | 13 | public class DecimalExpressionTest 14 | { 15 | @Test 16 | public void literals() 17 | { 18 | assertEquals(BigDecimal.valueOf(579, 4), evaluateToDecimal("toDecimal(123,4) + toDecimal(456,4)")); 19 | 20 | assertEquals(BigDecimal.valueOf(-333, 4), evaluateToDecimal("toDecimal(123,4) - toDecimal(456,4)")); 21 | 22 | assertEquals(BigDecimal.valueOf(56088, 8), evaluateToDecimal("toDecimal(123,4) * toDecimal(456,4)")); 23 | 24 | assertEquals( 25 | BigDecimal.valueOf(123.0).divide(BigDecimal.valueOf(456.0), MathContext.DECIMAL128), 26 | evaluateToDecimal("toDecimal(123, 4) / toDecimal(456, 4)")); 27 | } 28 | 29 | @Test 30 | public void expressions() 31 | { 32 | EvalContext context = new SimpleEvalContext(); 33 | context.setVariable("a", new DecimalValue(123456, 2)); 34 | context.setVariable("b", new DecimalValue(111, 2)); 35 | 36 | assertEquals( 37 | BigDecimal.valueOf(123567, 2), this.evaluateToDecimalWithContext("a + b", context)); 38 | 39 | assertEquals( 40 | BigDecimal.valueOf(123345, 2), this.evaluateToDecimalWithContext("a - b", context)); 41 | 42 | assertEquals( 43 | BigDecimal.valueOf(13703616, 4), this.evaluateToDecimalWithContext("a * b", context)); 44 | 45 | assertEquals( 46 | BigDecimal.valueOf(123456).divide(BigDecimal.valueOf(111), MathContext.DECIMAL128), 47 | this.evaluateToDecimalWithContext("a / b", context)); 48 | } 49 | 50 | @Test 51 | public void mixedTypes() 52 | { 53 | assertEquals(BigDecimal.valueOf(20123, 4), evaluateToDecimal("toDecimal(123,4) + 2")); 54 | assertEquals(BigDecimal.valueOf(2623, 4), evaluateToDecimal("toDecimal(123,4) + 0.25")); 55 | } 56 | 57 | private BigDecimal evaluateToDecimalWithContext(String expression, EvalContext context) 58 | { 59 | return ((DecimalValue) evaluateScriptWithContext(expression, context)).decimalValue(); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dsl/function/RuntimeAddedFunctionTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.function; 2 | 3 | import io.github.vmzakharov.ecdataframe.ExpressionTestUtil; 4 | import io.github.vmzakharov.ecdataframe.dsl.EvalContext; 5 | import io.github.vmzakharov.ecdataframe.dsl.value.LongValue; 6 | import io.github.vmzakharov.ecdataframe.dsl.value.Value; 7 | import io.github.vmzakharov.ecdataframe.dsl.value.ValueType; 8 | import org.eclipse.collections.api.list.ListIterable; 9 | import org.eclipse.collections.impl.factory.Lists; 10 | 11 | import org.junit.jupiter.api.Test; 12 | 13 | import static org.junit.jupiter.api.Assertions.*; 14 | 15 | public class RuntimeAddedFunctionTest 16 | { 17 | @Test 18 | public void simple() 19 | { 20 | BuiltInFunctions.addFunctionDescriptor(new IntrinsicFunctionDescriptor("plusTwo", Lists.immutable.of("number")) 21 | { 22 | @Override 23 | public Value evaluate(EvalContext context) 24 | { 25 | return new LongValue( 26 | ((LongValue) context.getVariable("number")).longValue() + 2 27 | ); 28 | } 29 | 30 | @Override 31 | public ValueType returnType(ListIterable parameterTypes) 32 | { 33 | return ValueType.LONG; 34 | } 35 | }); 36 | 37 | assertEquals(5, ExpressionTestUtil.evaluateToLong("plusTwo(3)")); 38 | assertEquals(4, ExpressionTestUtil.evaluateToLong("abs(plusTwo(-6))")); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dsl/projection/Donut.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.projection; 2 | 3 | import java.math.BigDecimal; 4 | 5 | public record Donut(String name, float price, int calories) 6 | { 7 | public double priceAsDouble() 8 | { 9 | return this.price; 10 | } 11 | 12 | public BigDecimal priceAsDecimal() 13 | { 14 | return BigDecimal.valueOf(this.price); 15 | } 16 | 17 | public long caloriesAsLong() 18 | { 19 | return this.calories; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dsl/projection/Person.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.projection; 2 | 3 | import java.time.LocalDate; 4 | import java.time.LocalDateTime; 5 | 6 | public record Person(String name, long luckyNumber, double temperature, LocalDate specialDate, FavoriteFood favoriteFood) 7 | { 8 | public Person(String newName, long newLuckyNumber, double newTemperature, LocalDate newSpecialDate, 9 | String favoriteFoodName, LocalDateTime timeLastEaten) 10 | { 11 | this(newName, newLuckyNumber, newTemperature, newSpecialDate, new FavoriteFood(favoriteFoodName, timeLastEaten)); 12 | } 13 | 14 | public Long bigLuckyNumber() 15 | { 16 | return this.luckyNumber; 17 | } 18 | 19 | public Double bigTemperature() 20 | { 21 | return this.temperature; 22 | } 23 | 24 | public record FavoriteFood(String description, LocalDateTime lastEaten) 25 | { 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/io/github/vmzakharov/ecdataframe/dsl/value/ValueTest.java: -------------------------------------------------------------------------------- 1 | package io.github.vmzakharov.ecdataframe.dsl.value; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | public class ValueTest 8 | { 9 | @Test 10 | public void booleansMakeSense() 11 | { 12 | assertTrue(BooleanValue.TRUE.isTrue()); 13 | assertFalse(BooleanValue.TRUE.isFalse()); 14 | 15 | assertTrue(BooleanValue.FALSE.isFalse()); 16 | assertFalse(BooleanValue.FALSE.isTrue()); 17 | 18 | assertSame(BooleanValue.TRUE, BooleanValue.valueOf(true)); 19 | assertSame(BooleanValue.FALSE, BooleanValue.valueOf(false)); 20 | } 21 | 22 | @Test 23 | public void voidValueMakesSense() 24 | { 25 | assertTrue(Value.VOID.isVoid()); 26 | } 27 | 28 | @Test 29 | public void stringValuesWrapNulls() 30 | { 31 | assertThrows(RuntimeException.class, () -> new StringValue(null)); 32 | } 33 | 34 | @Test 35 | public void decimalValuesCannotWrapNulls() 36 | { 37 | assertThrows(RuntimeException.class, () -> new DecimalValue(null)); 38 | } 39 | 40 | @Test 41 | public void dateValuesCannotWrapNulls() 42 | { 43 | assertThrows(RuntimeException.class, () -> new DateValue(null)); 44 | } 45 | 46 | @Test 47 | public void dateTimeValuesCannotWrapNulls() 48 | { 49 | assertThrows(RuntimeException.class, () -> new DateTimeValue(null)); 50 | } 51 | 52 | @Test 53 | public void dataFrameValuesCannotWrapNulls() 54 | { 55 | assertThrows(RuntimeException.class, () -> new DataFrameValue(null)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/resources/Messages.properties: -------------------------------------------------------------------------------- 1 | GREETING = Hello, ${name}! How are you? 2 | SALUTATION = GREETINGS PROFESSOR ${lastName}. 3 | -------------------------------------------------------------------------------- /src/test/resources/employees.csv: -------------------------------------------------------------------------------- 1 | Name,EmployeeId,HireDate,Dept,Salary 2 | "Alice",1234,2020-01-01,"Accounting",110000.00 3 | "Bob",1233,2010-01-01,"Bee-bee-boo-boo",100000.00 4 | "Carl",10000,2005-11-21,"Controllers",130000.00 5 | "Diane",10001,2012-09-20,"",130000.00 6 | "Ed",10002,,,0.00 7 | --------------------------------------------------------------------------------