Skip to content

fulibTables provides model query and transformation mechanisms for fulib object models.

License

Notifications You must be signed in to change notification settings

fujaba/fulibTables

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fulibTables

Build Status Java CI Download javadoc

fulibTables provides model query and transformation mechanisms for fulib object models.

Installation

build.gradle:

repositories {
    mavenCentral()
    jcenter()
}
dependencies {
    // https://mvnrepository.com/artifact/org.fulib/fulibTables
    compile group: 'org.fulib', name: 'fulibTables', version: '1.4.0'
}

Usage

To demonstrate tables we start with an extended version of the StudyRight University class model:

ClassModelBuilder mb = Fulib.classModelBuilder("uniks.studyright.model", "src/test/java");

ClassBuilder university = mb.buildClass("University").buildAttribute("name", STRING);

ClassBuilder student = mb.buildClass("Student")
                         .buildAttribute("name", STRING)
                         .buildAttribute("studentId", STRING)
                         .buildAttribute("credits", DOUBLE)
                         .buildAttribute("points", DOUBLE)
                         .buildAttribute("motivation", DOUBLE);

ClassBuilder room = mb.buildClass("Room")
                      .buildAttribute("roomNo", STRING)
                      .buildAttribute("topic", STRING)
                      .buildAttribute("credits", DOUBLE);

ClassBuilder assignment = mb.buildClass("Assignment")
                            .buildAttribute("task", STRING)
                            .buildAttribute("points", DOUBLE);

university.buildAssociation(student, "students", MANY, "uni", ONE);
university.buildAssociation(room, "rooms", MANY, "uni", ONE);
room.buildAssociation(student, "students", MANY, "in", ONE);
room.buildAssociation(assignment, "assignments", MANY, "room", ONE);
student.buildAssociation(assignment, "done", MANY, "students", MANY);
student.buildAssociation(student, "friends", MANY, "friends", MANY);

ClassModel model = mb.getClassModel();

FulibTools.classDiagrams().dumpSVG(model, "doc/images/MainClassDiagram.svg");
FulibTools.classDiagrams().dumpPng(model, "doc/images/MainClassDiagram.png");

Fulib.generator().generate(model);

Rendered as a class diagram the extended class model looks like:

simple class diagram

Once the generated code is compiled, we may construct some objects:

// build object structure
University studyRight = new University().setName("Study Right");

Room mathRoom = new Room().setRoomNo("wa1337").setTopic("Math").setCredits(42.0).setUni(studyRight);
Room artsRoom = new Room().setRoomNo("wa1338").setTopic("Arts").setCredits(23.0).setUni(studyRight);
Room sportsRoom = new Room().setRoomNo("wa1339").setTopic("Football").setUni(studyRight);

Assignment integrals = new Assignment().setTask("integrals").setPoints(42).setRoom(mathRoom);
Assignment matrix = new Assignment().setTask("matrices").setPoints(23).setRoom(mathRoom);
Assignment drawings = new Assignment().setTask("drawings").setPoints(12).setRoom(artsRoom);
Assignment sculptures = new Assignment().setTask("sculptures").setPoints(12).setRoom(artsRoom);

Student alice = new Student().setStudentId("m4242").setName("Alice").setUni(studyRight).setIn(mathRoom).withDone(integrals);
Student bob   = new Student().setStudentId("m2323").setName("Bobby").setUni(studyRight).setIn(artsRoom).withFriends(alice);
Student carli = new Student().setStudentId("m2323").setName("Carli").setUni(studyRight).setIn(mathRoom);

FulibTools.objectDiagrams().dumpSVG("doc/images/studyRightObjects.svg", studyRight);
FulibTools.objectDiagrams().dumpPng("doc/images/studyRightObjects.png", studyRight);

This results in:

object diagram

fulibTables provides the class ObjectTable, which allows us to perform some table operations:

// some table stuff
ObjectTable<University> universityTable = new ObjectTable<>("University", studyRight);
ObjectTable<Room> roomsTable = universityTable.expandAll("Room", University::getRooms);
ObjectTable<Assignment> assignmentsTable = roomsTable.expandAll("Assignment", Room::getAssignments);

The first line generates a table with just one University column and one row containing the StudyRight object.

University
Study Right

The second line extends the university table with a Room column. For each row of the old table we look up the university u contained in the University column (in our case there is just one row containing the studyRight object in its University column). For each university object u, we look up each room r attached to it. For each university object u and room r pair, we create a new row in the resulting room table.

University Room
Study Right wa1337 Math
Study Right wa1338 Arts
Study Right wa1339 Football

The third line expands the room table with the attached assignments. Again, we loop through the rows of the room table and look up the university u contained in the University column and the room r contained in the Room column. Then, for each assignment a attached to room r, we create a result row containing u, r, and a.

The table below shows the current result:

University Room Assignment
Study Right wa1337 Math integrals
Study Right wa1337 Math matrices
Study Right wa1338 Arts drawings
Study Right wa1338 Arts sculptures

Note that all three variables universityTable, roomsTable, and assignmentsTable refer to the same internal table object. However, they each refer to the specific column where the next expand operation applies.

Each table wrapper is also an Iterable listing all objects of the corresponding column. Thus, to sum up the points of all assignments of our table, we may:

double sum = 0;
for (Assignment a : assignmentsTable)
{
   sum += a.getPoints();
}
assertThat(sum, equalTo(89.0));

Alternatively, we may expand the assignment table by a Points column:

doubleTable pointsTable = assignmentsTable.expand("Points", Assignment::getPoints).as(doubleTable.class);
sum = pointsTable.sum();
assertThat(roomsTable.rowCount(), equalTo(4));
assertThat(assignmentsTable.rowCount(), equalTo(4));
assertThat(sum, equalTo(89.0));
University Room Assignment Points
Study Right wa1337 Math integrals 42.0
Study Right wa1337 Math matrices 23.0
Study Right wa1338 Arts drawings 12.0
Study Right wa1338 Arts sculptures 12.0

The resulting points table has a sum method that sums up all double values contained in the corresponding column.

To further expand our table we might add students that are in rooms:

ObjectTable<Student> students = roomsTable.expandAll("Student", Room::getStudents);
assertThat(students.rowCount(), equalTo(6));
University Room Assignment Points Student
Study Right wa1337 Math integrals 42.0 Alice m4242
Study Right wa1337 Math integrals 42.0 Carli m2323
Study Right wa1337 Math matrices 23.0 Alice m4242
Study Right wa1337 Math matrices 23.0 Carli m2323
Study Right wa1338 Arts drawings 12.0 Bobby m2323
Study Right wa1338 Arts sculptures 12.0 Bobby m2323

The resulting table has the cross product of assignments and students for each room.

In addition to the cross product we may select a subset of the table rows using a filter operation:

assignmentsTable.filter(a -> a.getPoints() <= 30);
assertThat(students.rowCount(), equalTo(4));
University Room Assignment Points Student
Study Right wa1337 Math matrices 23.0 Alice m4242
Study Right wa1337 Math matrices 23.0 Carli m2323
Study Right wa1338 Arts drawings 12.0 Bobby m2323
Study Right wa1338 Arts sculptures 12.0 Bobby m2323

Alternatively, we may filter by rows:

// filter row
universityTable = new ObjectTable<>("University", studyRight);
roomsTable = universityTable.expandAll("Room", University::getRooms);
students = roomsTable.expandAll("Student", Room::getStudents);
assignmentsTable = roomsTable.expandAll("Assignment", Room::getAssignments);

students.filterRows(row -> {
   Student studi = (Student) row.get("Student");
   Assignment assignment = (Assignment) row.get("Assignment");
   return studi.getDone().contains(assignment);
});

assertThat(students.rowCount(), equalTo(1));
University Room Student Assignment
Study Right wa1337 Math Alice m4242 integrals

Note, when we did the filter by assignment, our internal table was reduced to 4 rows. To have a full table for the filter by row operation, we had to reconstruct that full table.

Above row filter requires that the current student has done the current assignment. This filter condition may also be expressed by a hasLink operation:

// filter row
universityTable = new ObjectTable<>("University", studyRight);
roomsTable = universityTable.expandAll("Room", University::getRooms);
students = roomsTable.expandAll("Student", Room::getStudents);
assignmentsTable = roomsTable.expandAll("Assignment", Room::getAssignments);
students.hasLink(Student.PROPERTY_done, assignmentsTable);

assertThat(students.rowCount(), equalTo(1));
University Room Student Assignment
Study Right wa1337 Math Alice m4242 integrals

The filterRows operations may also be used to modify the current model:

universityTable = new ObjectTable<>("University", studyRight);
roomsTable = universityTable.expandAll("Room", University::getRooms);
students = roomsTable.expandAll("Student", Room::getStudents);
assignmentsTable = roomsTable.expandAll("Assignment", Room::getAssignments);

// do current assignments
students.filterRows(row -> {
   Student studi = (Student) row.get("Student");
   Assignment assignment = (Assignment) row.get("Assignment");
   studi.withDone(assignment);
   return true;
});

FulibTools.objectDiagrams().dumpPng("doc/images/studyRightObjectsMoreDone4Tables.png", studyRight);

assertThat(alice.getDone().size(), equalTo(2));
assertThat(integrals.getStudents().contains(alice), is(true));

// show size of done
universityTable.derive("noOfDone", row -> {
   Student studi = (Student) row.get("Student");
   return studi.getDone().size();
});

// show done
students.expandAll("Done", Student::getDone);
University Room Student Assignment noOfDone Done
Study Right wa1337 Math Alice m4242 integrals 2 integrals
Study Right wa1337 Math Alice m4242 integrals 2 matrices
Study Right wa1337 Math Alice m4242 matrices 2 integrals
Study Right wa1337 Math Alice m4242 matrices 2 matrices
Study Right wa1337 Math Carli m2323 integrals 2 integrals
Study Right wa1337 Math Carli m2323 integrals 2 matrices
Study Right wa1337 Math Carli m2323 matrices 2 integrals
Study Right wa1337 Math Carli m2323 matrices 2 matrices
Study Right wa1338 Arts Bobby m2323 drawings 2 drawings
Study Right wa1338 Arts Bobby m2323 drawings 2 sculptures
Study Right wa1338 Arts Bobby m2323 sculptures 2 drawings
Study Right wa1338 Arts Bobby m2323 sculptures 2 sculptures

object diagram

As the current table contains some confusing cross products, let us drop the Assignment column:

universityTable.dropColumns("Assignment");
University Room Student noOfDone Done
Study Right wa1337 Math Alice m4242 2 integrals
Study Right wa1337 Math Alice m4242 2 matrices
Study Right wa1337 Math Carli m2323 2 integrals
Study Right wa1337 Math Carli m2323 2 matrices
Study Right wa1338 Arts Bobby m2323 2 drawings
Study Right wa1338 Arts Bobby m2323 2 sculptures

Alternatively, we may select the columns we are interested in:

students.selectColumns("Student", "Done");
assertThat(students.rowCount(), equalTo(6));
Student Done
Alice m4242 integrals
Alice m4242 matrices
Carli m2323 integrals
Carli m2323 matrices
Bobby m2323 drawings
Bobby m2323 sculptures

If you want to update all elements of a certain column, nested tables may come in handy:

universityTable = new ObjectTable<>("University", studyRight);
students = universityTable.expandAll("Students", University::getStudents);
students.derive("Credits", row -> {
   Student student = (Student) row.get("Students");
   double pointSum = new ObjectTable<>(student)
      .expandAll("Assignments", Student::getDone)
      .expand("Points", Assignment::getPoints)
      .as(doubleTable.class)
      .sum();
   student.setCredits(pointSum);
   return pointSum;
});
students.derive("Done", row -> {
   Student student = (Student) row.get("Students");
   return new ObjectTable<>("Students", student)
      .expandAll("Assignments", Student::getDone)
      .expand("Tasks", Assignment::getTask)
      .as(StringTable.class)
      .join(", ");
});

In the third last line the expandTopic operation adds a column with the topic names of the corresponding assignments to the local table. On string columns, you can call join to concatenate all strings.

University Students Credits Done
Study Right Alice m4242 65.0 integrals, matrices
Study Right Bobby m2323 24.0 drawings, sculptures
Study Right Carli m2323 65.0 integrals, matrices

object diagram

License

MIT