This project is aimed to experiment with the implementation of heterogeneous yet type-safe table structure.
As of now, the API makes able to:
- create heterogeneous tables,
- modify them and their content,
- query their content in a type-safe way.
Because code is better than words, here is a quick overview of the API:
ColumnId<Integer> AGE = id("age", Integer.class);
ColumnId<String> NAME = id("name", String.class);
Table people = new DataTable();
people.columns()
.create(NAME, "Luc", "Baptiste", "Marie")
.create(AGE, 23, 32, 42);
Table adultsWhoseNameEndsWithLetterE = people.filter(row ->
row.get(AGE) > 18 &&
row.get(NAME).startsWith("E")
);
Ids can be seen as references, as keys used to identify specific columns within a table. They are tricks used to provide a type-safe way to access the content of a table.
Basically, they are simple data structure containing:
- a
type
, that is the Java class of the column's elements - a
header
, that is the name of the column.
An id is represented by the ColumnId class and can be created with the id
static method :
// name is an id that matches any column containing Strings and which header is "col1"
ColumnId<String> name = ColumnId.id("col1", String.class);
In the following document, ids are used everywhere. However, although they are the advised way to deal with a table, they can always be replaced by indexes or column names.
Creation of a new Table consists of an easy one-liner :
Table people = new DataTable();
A table can be filled either by adding new columns :
people.columns()
.create("Name", String.class, "Luc", "Baptiste", "Marie")
.create("Age", Integer.class, 23, 32, 42);
or by adding new rows :
people.rows()
.create("Julie", 12)
.create("Mathieu", 67);
The table resulting of the above statements is the following :
+----------+-----+
| Name | Age |
+----------+-----|
| Luc | 23 |
| Baptiste | 32 |
| Marie | 42 |
| Julie | 12 |
| Mathieu | 67 |
+----------+-----+
A table can be emptied either by removing columns :
people.columns()
.remove("age");
or by removing rows :
people.rows()
.remove(4)
.remove(2);
Notice how the last row has been removed first in order to avoid bugs related to changes of ids. The table now looks like :
+----------+
| Name |
+----------+
| Luc |
| Baptiste |
| Julie |
+----------+
The filter
method makes easy to retrieve the rows of a table that match a specific criterion:
import static fr.kazejiyu.generic.datatable.core.impl.ColumnId.*;
public class Main {
// Reference table's columns
private static final ColumnId<Integer> AGE = id("age", Integer.class);
private static final ColumnId<String> NAME = id("name", String.class);
Table adultsWhoseNameEndsWithLetterE(Table people) {
return people.filter(row ->
row.get(AGE) > 18 &&
row.get(NAME).startsWith("E")
);
}
}
For more complex cases, the API also brings a SQL-like DSL that makes possible to apply a same filter to multiple columns. It can be used as follows :
import static fr.kazejiyu.generic.datatable.core.impl.ColumnId.*;
public class Main {
// Reference table's columns
private static final ColumnId<Integer> AGE = id("age", Integer.class);
private static final ColumnId<String> NAME = id("name", String.class);
private static final ColumnId<String> SURENAME = id("surename", String.class);
// Retrieve all european adults whose both name and surename ends with the letter "e"
Table adultsWhoseNameEndsWithLetterE(Table people) {
return Query
.from(people)
.where(s(NAME, SURENAME)).endsWith("e")
.and(AGE).gt(18)
.and(COUNTRY).match(Country::isInEurope)
.select();
}
}
Notice how the s
method is used to specify that the columns concerning by the WHERE
clause contain String instances.
That makes possible to use tailored methods (such as endsWith
in the above example) ; because of Java's type erasure, this trick is mandatory.
This static method is defined in the ColumnId
class, as well as the methods:
n
: used to apply a same filter to several columns of numbers,b
: used to apply a same filter to several columns of booleans.