An open API service indexing awesome lists of open source software.

https://github.com/codegeny/codegeny-beans

Library which provides meta-model, diffs, paths... for java beans
https://github.com/codegeny/codegeny-beans

beans diff java

Last synced: 5 months ago
JSON representation

Library which provides meta-model, diffs, paths... for java beans

Awesome Lists containing this project

README

          

![Status experimental](https://img.shields.io/badge/status-experimental-red.svg)
[![Build Status](https://img.shields.io/travis/codegeny/codegeny-beans.svg)](https://travis-ci.org/codegeny/codegeny-beans)
[![Maven Central](https://img.shields.io/maven-central/v/org.codegeny/codegeny-beans.svg)](https://mvnrepository.com/artifact/org.codegeny/codegeny-beans)
[![Code Coverage](https://img.shields.io/codecov/c/github/codegeny/codegeny-beans.svg)](https://codecov.io/gh/codegeny/codegeny-beans)
[![Code Analysis](https://img.shields.io/codacy/grade/2a447b2e20e34b628cef941f7619e184.svg)](https://www.codacy.com/app/codegeny/codegeny-beans)

# codegeny-beans

Express your bean structure in a reflection-free, dependency-free and type-safe model; get generic diff/comparison/toString/toJson...

This project makes heavy (maybe too much) use of the visitor pattern to avoid casting where possible.

## The Model interface

A `Model` is used to express the hierarchical structure of an object in a type-safe and reflection-free way.

In turn, once you have your model defined, you can use it to generate a toString for your object, compare objects, diff objects, hash objects...

The `Model` interface represents a node in the hierarchical structure for an object of type `T` and can be of one of the following concrete classes:

- `ValueModel` which represents an atomic value of type `T`. Atomic values must be comparable or a comparator must be given to the `ValueModel`.
- `ListModel` represents a list-like value (of type `L`) of other models (of type `E`). Note that `L` is not required to be of type `List` but then a transformation function (`Function>`) must be given to the `ListModel`.
- `SetModel` represents a set-like value (of type `S`) of other models (of type `E`). Note that `S` is not required to be of type `Set` but then a transformation function (`Function>`) must be given to the `SetModel`.
- `MapModel` represents a map-like value (of type `M`) of keys (`K`) and values (`V`). As with lists and sets, `M` is not required to be of type `Map` but a `Function>` must be given to the `MapModel`.
- `BeanModel` represents a bean of type `B`. A `BeanModel` contains a map of properties (`Map>`).

The `Model` interface accepts `DiffVisitor`s (`R` being the result type).

### Example

Lets define the following classes:

```java
public class Person {

private String firstName;
private String lastName;
private LocalDate birthDate;
private Address mainAddress;
private Set

formerAddresses;

// getters and setters
}

public class Address {

private String street;
private String houseNumber;
private String zipCode;
private String city;
private Country country;

// getters and setters
}

public enum Country {

USA, FRANCE, GERMANY, ITALY, SPAIN, JAPAN...
}
```

The structure of the class Person can be defined as follows:

```java
Model

addressModel = Model.bean(Address.class,
Model.property("street", Address::getStreet, Model.STRING),
Model.property("houseNumber", Address::getHouseNumber, Model.STRING),
Model.property("city", Address::getZipCode, Model.STRING),
Model.property("country", Address::getCity, Model.STRING),
Model.property("street", Address::getCountry, Model.value(Country.class))
);

Model personModel = Model.bean(Person.class,
Model.property("firstName", Person::getFirstName, Model.STRING),
Model.property("lastName", Person::getLastName, Model.STRING),
Model.property("birthDate", Person::getBirthDate, Model.value(LocalDate.class)),
Model.property("mainAddress", Person::getMainAddress, addressModel),
Model.property("formerAddresses", Person::getFormerAddresses, Model.set(addressModel))
);
```

Note that instead of using getters method references (`Person::getFirstName`), a lambda could also be used (`(Person p) -> p.getFirstName().toUpperCase()`).
This allows to transform the values before they are used (if that is necessary).

With that `Model` you could do the following:

```java
Person person = ... // create some person instance

String string = personModel.toString(person); // create a generic string representation for person

int comparison = personModel.compare(person, anotherPerson); // compare person with anotherPerson by comparing fields in the order they were defined (firstName, lastName, birthDate, mainAddress.street, mainAddress.houseNumber...)

personModel.get(person, Path.of("mainAddress", "city")); // extract person.mainAddress.city

Diff diff = personModel.accept(new ComputeDiffModelVisitor<>(person, anotherPerson)); // returns a Diff, see below for more explanation.
```

## The Diff interface

The `Diff` interface represents the difference between two instances of `T` (left and right).

A diff:
- has a status in (`ADDED`, `REMOVED`, `UNCHANGED`, `MODIFIED`)
- has left and right values of type `T`
- can be visited by a `DiffVisitor`

A diff can be of one of the following concrete classes:
- `SimpleDiff` representing a diff between two atomic values of type `T`
- `ListDiff` representing a diff between two list-like values of type `C` containing elements of type `E`
- `MapDiff` representing a diff between two map-like values of type `M` containing entries with key of type `K` and value of type `V`
- `BeanDiff` representing a diff between two beans of type `B`

### Basic example

```
left = President {
firstNames: ['George', 'Herbert', 'Walker'],
lastName: 'Bush',
birthYear: 1924,
electedYears: [1989]
}

right = President {
firstNames: ['George', 'Walker'],
lastName: 'Bush',
birthYear: 1946,
electedYears: [2001, 2005]
}

diff(left, right) = BeanDiff(status = MODIFIED, left = ..., right = ...) {
'firstNames': ListDiff, String>(status = MODIFIED, left = ..., right = ...) [
SimpleDiff(status = UNCHANGED, left = 'George', right = 'George'),
SimpleDiff(status = REMOVED, left = 'Herbert', right = null),
SimpleDiff(status = UNCHANGED, left = 'Walker', right = 'Walker'),
],
'lastName': SimpleDiff(status = UNCHANGED, left = 'Bush', right = 'Bush'),
'birthYear': SimpleDiff(status = MODIFIED, left = 1924, right = 1946),
'electedYears': ListDiff, Integer>(status = MODIFIED, left = ..., right = ...) [
SimpleDiff(status = REMOVED, left = 1989, right = null),
SimpleDiff(status = ADDED, left = null, right = 2001),
SimpleDiff(status = ADDED, left = null, right = 2005),
]
}
```

## Paths

A path object which can be used by both `Model` and `Diff` is also available:

```java
Path path = Path.of("previousAddresses", 2, "street");

Person left = ...
Person right = ...
Model personModel = ...
Diff personDiff = personModel.diff(left, right);

Object object = personModel.get(left, path); // extract value from left
Diff> diff = personDiff.get(path); // extract diff
```

## Examples

Examples can be found on the [codegeny-beans-examples repo](https://github.com/codegeny/codegeny-beans-examples).