diff --git a/core/README.md b/core/README.md index 5f07b82..2b1d980 100644 --- a/core/README.md +++ b/core/README.md @@ -10,7 +10,7 @@ Bistro-core is a core library of Bistro for schema management, data representati First of all, it is necessary to create a *schema* which can be thought of as a database and will be a collection of all other elements and parameters: ```java -Bistro schema = new Bistro("My Bistro"); +Schema schema = new Schema("My Schema"); ``` A schema like tables and columns has an arbitrary (case sensitive) name. The schema is then used to create and access other elements as well as perform various operations with data. @@ -73,7 +73,7 @@ For example, we could define a calculate column which increments the value store Column calc = schema.createColumn("length", table, objects); calc.calc( (p, o) -> ((String)p[0]).length(), // How to compute - Arrays.asList(name) // Parameters for computing + new Column[] {name}) // Parameters for computing ); ``` The first parameter is a function which takes two arguments. The first argument `p` is an array of outputs of other columns that have to be processed. The second argument `o` is the current output of this same column which is not used for calculate columns. The second parameter of the definition specifies the input columns the outputs of which have to be processed. In this example, we use a previously defined no-definition column the outputs of which will be incremented. The size of the `p` array has to be equal to the length of the second parameter. @@ -85,7 +85,7 @@ calc.eval(); Now, if there were no errors, we can retrieve the output values: ```java value = calc.getValue(1); // value = 3 -value = calc.getValue(1); // value = 7 +value = calc.getValue(2); // value = 7 ``` There exist also other ways to define calculate columns which can be more convenient in different situations, for example, in the case of complex arithmetic operations or in the case of complex computations implemented programmatically. Note also that column outputs could contain `null` and all lambda functions must guarantee the validity of its computations including null-safety and type-safety. @@ -110,8 +110,8 @@ This property however cannot be used to access the elements of the "Table". Ther ```java Column link = schema.createColumn("link to table", facts, table); link.link( - Arrays.asList(name) // Columns to be used for searching (in the type table) - Arrays.asList(group) // Columns providing criteria for search (in this input table) + new Column[] {name}, // Columns to be used for searching (in the type table) + new Column[] {group} // Columns providing criteria for search (in this input table) ); ``` This definition essentially means that the new column will reference elements of its type table which have the same `name` as this table `group` column. @@ -178,7 +178,7 @@ We can find the sum of the measure for each element in the "Table" using this ac Column sums = schema.createColumn("sum measure", table, objects); sums.accu( (p, o) -> (Double)o + (Double)p[0], // Add the measure for each new fact - Arrays.asList(measure) // Measure + new Column[] {measure} // Measure link // Grouping column ); diff --git a/core/src/main/java/org/conceptoriented/bistro/core/Column.java b/core/src/main/java/org/conceptoriented/bistro/core/Column.java index ef41f2e..838cc69 100644 --- a/core/src/main/java/org/conceptoriented/bistro/core/Column.java +++ b/core/src/main/java/org/conceptoriented/bistro/core/Column.java @@ -359,11 +359,11 @@ public void accu(Evaluator accuEval, ColumnPath[] params, ColumnPath accuPath) { } // Evaluator + parameters - public void accu(Evaluator accuEval, Column[] params, ColumnPath accuPath) { + public void accu(Evaluator accuEval, Column[] params, Column accuPath) { this.setDefinitionType(ColumnDefinitionType.ACCU); // Reset definition Expression accuExpr = new Expr(accuEval, params); - this.definition = new ColumnDefinitionAccu(this, accuExpr, accuPath); + this.definition = new ColumnDefinitionAccu(this, accuExpr, new ColumnPath(accuPath)); if(this.isInCyle()) { this.definitionErrors.add(new BistroError(BistroErrorCode.DEFINITION_ERROR, "Cyclic dependency.", "This column depends on itself directly or indirectly.")); diff --git a/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionBase.java b/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionBase.java index d55556c..9b82188 100644 --- a/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionBase.java +++ b/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionBase.java @@ -48,7 +48,11 @@ protected void evaluateExpr(Expression expr, ColumnPath accuLinkPath) { result = expr.evaluate(paramValues, out); } catch(BistroError e) { - definitionErrors.add(e); + this.definitionErrors.add(e); + return; + } + catch(Exception e) { + this.definitionErrors.add( new BistroError(BistroErrorCode.EVALUATION_ERROR, e.getMessage(), "") ); return; } diff --git a/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionLinkExprs.java b/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionLinkExprs.java index a30717d..62093de 100644 --- a/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionLinkExprs.java +++ b/core/src/main/java/org/conceptoriented/bistro/core/ColumnDefinitionLinkExprs.java @@ -94,6 +94,10 @@ protected void evaluateLink(List columns, List exprs) { definitionErrors.add(e); return; } + catch(Exception e) { + this.definitionErrors.add( new BistroError(BistroErrorCode.EVALUATION_ERROR, e.getMessage(), "") ); + return; + } rhsResults.set(mmbrNo, result); } diff --git a/core/src/main/java/org/conceptoriented/bistro/core/Expr.java b/core/src/main/java/org/conceptoriented/bistro/core/Expr.java index 4a41697..977da56 100644 --- a/core/src/main/java/org/conceptoriented/bistro/core/Expr.java +++ b/core/src/main/java/org/conceptoriented/bistro/core/Expr.java @@ -21,11 +21,14 @@ public class Expr implements Expression { public Expr(Evaluator eval, ColumnPath[] params) { this.evaluateLambda = eval; + + if(params == null) params = new ColumnPath[]{}; this.setParameterPaths(Arrays.asList(params)); } public Expr(Evaluator eval, Column[] params) { this.evaluateLambda = eval; + if(params == null) params = new Column[]{}; List paths = new ArrayList<>(); for(int i=0; i + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/misc.xml b/examples/.idea/misc.xml new file mode 100644 index 0000000..e208459 --- /dev/null +++ b/examples/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/examples/.idea/modules.xml b/examples/.idea/modules.xml new file mode 100644 index 0000000..c23b383 --- /dev/null +++ b/examples/.idea/modules.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/modules/examples_main.iml b/examples/.idea/modules/examples_main.iml new file mode 100644 index 0000000..b5731fd --- /dev/null +++ b/examples/.idea/modules/examples_main.iml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/.idea/modules/examples_test.iml b/examples/.idea/modules/examples_test.iml new file mode 100644 index 0000000..0fac488 --- /dev/null +++ b/examples/.idea/modules/examples_test.iml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/build.gradle b/examples/build.gradle new file mode 100644 index 0000000..0157aaf --- /dev/null +++ b/examples/build.gradle @@ -0,0 +1,90 @@ +buildscript { + repositories { + flatDir { + dirs "$rootProject.projectDir" + } + mavenLocal() + mavenCentral() + } + dependencies { + } +} + +apply plugin: 'java' +apply plugin: 'antlr' +apply plugin: 'eclipse' +apply plugin: 'maven-publish' + +jar { + baseName = 'bistro-examples' + version = '0.3.0' +} + +repositories { + mavenLocal() + mavenCentral() + //jcenter() +} + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +group = 'org.conceptoriented' +archivesBaseName = 'bistro-examples' +version = '0.3.0' + +dependencies { + compile("org.conceptoriented:bistro-core:0.3.0") + compile("org.conceptoriented:bistro-formula:0.3.0") + + antlr("org.antlr:antlr4:4.7") + + compile("org.antlr:antlr4-runtime:4.7") + + compile("net.objecthunter:exp4j:0.4.8") + compile("com.udojava:EvalEx:1.11") + + testCompile("junit:junit:4.12") +} + +task wrapper(type: Wrapper) { + gradleVersion = '4.1' +} + +// Install into (local) maven repository +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + + // Set the base name of the artifacts + //groupId 'org.conceptoriented' + artifactId 'bistro-examples' + //version '0.3.0' + + //artifact jar + artifact sourcesJar + artifact javadocJar + } + } + repositories { + mavenLocal() + } +} + +// Generate archive with source files (to be installed into maven repository) +task sourcesJar(type: Jar, dependsOn: classes) { + classifier 'sources' + from sourceSets.main.allSource +} + +task javadocJar(type: Jar) { + classifier = 'javadoc' + from javadoc +} + +artifacts { + archives jar + archives sourcesJar + archives javadocJar +} diff --git a/examples/examples.iml b/examples/examples.iml new file mode 100644 index 0000000..c9d3a84 --- /dev/null +++ b/examples/examples.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/gradle/wrapper/gradle-wrapper.jar b/examples/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..b5daeaf Binary files /dev/null and b/examples/gradle/wrapper/gradle-wrapper.jar differ diff --git a/examples/gradle/wrapper/gradle-wrapper.properties b/examples/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..e34fe23 --- /dev/null +++ b/examples/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Oct 09 19:00:15 CEST 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-bin.zip diff --git a/examples/gradlew b/examples/gradlew new file mode 100644 index 0000000..4453cce --- /dev/null +++ b/examples/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/examples/gradlew.bat b/examples/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/examples/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/examples/src/main/java/org/conceptoriented/bistro/examples/Example1.java b/examples/src/main/java/org/conceptoriented/bistro/examples/Example1.java index cd3a40e..8484cac 100644 --- a/examples/src/main/java/org/conceptoriented/bistro/examples/Example1.java +++ b/examples/src/main/java/org/conceptoriented/bistro/examples/Example1.java @@ -2,9 +2,115 @@ import org.conceptoriented.bistro.core.*; +import java.util.Arrays; + public class Example1 { public static void main(String[] args) { - } + // + // Create schema + // + + Schema schema = new Schema("My Schema"); + + // + // Create tables + // + Table table; + + Table things = schema.createTable("THINGS"); + Table events = schema.createTable("EVENTS"); + + // Primitive tables are used as data types when defining columns + Table object = schema.getTable("Object"); + + // + // Create columns + // + Column column; + + // Each thing has a name + Column thingName = schema.createColumn("Name", things, object); + + // Each event stores name of the device it was sent from + Column eventThingName = schema.createColumn("Thing Name", events, object); + + // + // Data + // + + // Elements are appended to tables and are identified by an id + long id; + id = things.add(); // id = 0 + id = things.add(); // id = 1 + + // Data values are stored in columns as objects + thingName.setValue(0, "fridge"); + thingName.setValue(1, "oven"); + + // Values are read from columns as objects given their id + Object value; + value = thingName.getValue(0); // value = "fridge" + value = thingName.getValue(1); // value = "oven" + + // + // Calculate column + // + + // This column will compute the thing name length in characters + Column calc = schema.createColumn("Name Length", things, object); + calc.calc( + (p, o) -> ((String)p[0]).length(), // How to compute + new Column[]{thingName} // Parameters for computing + ); + + // + // Calculate column + // + + // Add some event data to aggregate + events.add(3); + eventThingName.setValue(0, "oven"); + eventThingName.setValue(1, "fridge"); + eventThingName.setValue(2, "oven"); + + // Link column finds its output in the output table + Column link = schema.createColumn("Thing", events, things); + link.link( + new Column[] {thingName}, // Columns to be used for searching (in the type table) + new Column[] {eventThingName} // Columns providing criteria for search (in this input table) + ); + + // + // Accumulate column + // + + Column counts = schema.createColumn("Event Count", things, object); + counts.accu( + (p, o) -> (Double)o + 1.0, // How to accumulate/update + null, // Nothing to aggregate except for counting + link // How to group/map facts to this table + ); + counts.setDefaultValue(0.0); // It will be used as an initial value + + // + // Evaluate and read values + // + + schema.eval(); // All 3 derived columns will be evaluated + + // Calculate column + value = calc.getValue(0); // value = 6 + value = calc.getValue(1); // value = 4 + + // Link column + value = link.getValue(0); // value = 1 + value = link.getValue(1); // value = 0 + value = link.getValue(2); // value = 1 + + // Accu column + value = counts.getValue(0); // 1 event from fridge + value = counts.getValue(1); // 2 events from oven + } }