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
- Host: GitHub
- URL: https://github.com/codegeny/codegeny-beans
- Owner: codegeny
- License: apache-2.0
- Created: 2017-08-07T14:05:27.000Z (almost 9 years ago)
- Default Branch: master
- Last Pushed: 2021-01-05T09:32:22.000Z (over 5 years ago)
- Last Synced: 2025-08-01T15:10:52.714Z (10 months ago)
- Topics: beans, diff, java
- Language: Java
- Homepage:
- Size: 267 KB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README

[](https://travis-ci.org/codegeny/codegeny-beans)
[](https://mvnrepository.com/artifact/org.codegeny/codegeny-beans)
[](https://codecov.io/gh/codegeny/codegeny-beans)
[](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).