Skip to content

Commit a9b8ea3

Browse files
committed
Merge branch 'release/1.0.0'
2 parents c935e5a + fd5eda4 commit a9b8ea3

37 files changed

+1028
-264
lines changed

README.md

Lines changed: 97 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,103 @@
1-
2-
# Stringify Object for Java
3-
4-
[![Build Status](https://travis-ci.org/wavesoftware/java-stringify-object.svg?branch=master)](https://travis-ci.org/wavesoftware/java-stringify-object) [![Quality Gate](https://sonar.wavesoftware.pl/api/badges/gate?key=pl.wavesoftware.utils:stringify-object)](https://sonar.wavesoftware.pl/dashboard/index/pl.wavesoftware.utils:stringify-object) [![Coverage Status](https://coveralls.io/repos/github/wavesoftware/java-stringify-object/badge.svg?branch=master)](https://coveralls.io/github/wavesoftware/java-stringify-object?branch=master)
5-
[![Maven Central](https://img.shields.io/maven-central/v/pl.wavesoftware.utils/stringify-object.svg)](https://bintray.com/bintray/jcenter/pl.wavesoftware.utils%3Astringify-object)
6-
7-
8-
A utility to safely inspect any Java Object as String representation. It's best to be used with JPA entity model to be dumped
9-
to log files.
10-
11-
It utilize `@Inspect` annotation to indicate which fields
12-
should be displayed. It has support for cycles, and Hibernate lazy loaded elements.
13-
14-
## Usage
15-
16-
```java
17-
// define fields to inspect
1+
# Stringify Object for Java
2+
3+
[![Build Status](https://travis-ci.org/wavesoftware/java-stringify-object.svg?branch=master)](https://travis-ci.org/wavesoftware/java-stringify-object) [![Quality Gate](https://sonar.wavesoftware.pl/api/badges/gate?key=pl.wavesoftware.utils:stringify-object)](https://sonar.wavesoftware.pl/dashboard/index/pl.wavesoftware.utils:stringify-object) [![Coverage Status](https://coveralls.io/repos/github/wavesoftware/java-stringify-object/badge.svg?branch=master)](https://coveralls.io/github/wavesoftware/java-stringify-object?branch=master) [![Maven Central](https://img.shields.io/maven-central/v/pl.wavesoftware.utils/stringify-object.svg)](https://bintray.com/bintray/jcenter/pl.wavesoftware.utils%3Astringify-object)
4+
5+
6+
A utility to safely inspect any Java Object as String representation. It's best to be used with domain model (also with JPA entities) with intention to dump those entities as text to log files.
7+
8+
It runs in two modes: `PROMISCUOUS` (by default) and `QUIET`. In `PROMISCUOUS` mode every defined field is automatically inspected, unless the field is annotated with `@DoNotInspect` annotation.
9+
In `QUIET` mode only fields annotated with `@Inspect` will gets inspected.
10+
11+
This library has proper support for object graph cycles, and JPA (Hibernate) lazy loaded elements.
12+
13+
## Usage
14+
15+
### In Promiscuous mode
16+
17+
```java
18+
// In PROMISCUOUS mode define fields to exclude
1819
class Person {
20+
private int id;
21+
@DisplayNull
22+
private Person parent;
23+
private List<Person> childs;
24+
private Account account;
25+
@Inspect(conditionally = IsInDevelopment.class)
26+
private String password;
27+
@DoNotInspect
28+
private String ignored;
29+
}
30+
31+
// inspect an object
32+
List<Person> people = query.getResultList();
33+
ObjectStringifier stringifier = new ObjectStringifier(people);
34+
stringifier.setMode(Mode.PROMISCUOUS);
35+
// stringifier.setBeanFactory(...);
36+
assert "<Person id=15, parent=<Person id=16, parent=null, "
37+
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
38+
+ "account=⁂Lazy>".equals(stringifier.toString());
39+
```
40+
41+
### In Quiet mode
42+
```java
43+
// In QUIET mode define fields to inspect
44+
class Person {
1945
@Inspect private int id;
20-
@Inspect private Person parent;
46+
@Inspect @DisplayNull private Person parent;
2147
@Inspect private List<Person> childs;
2248
@Inspect private Account account;
49+
private String ignored;
2350
}
24-
25-
// inspect an object
26-
List<Person> people = query.getResultList();
27-
ObjectStringifier stringifier = new ObjectStringifier(people);
28-
assert "<Person id=15, parent=<Person id=16, parent=null, "
29-
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
30-
+ "account=⁂Lazy>".equals(stringifier.toString());
31-
```
32-
33-
## Features
34-
35-
* String representation of any Java class
36-
* Fine tuning of which fields to display
37-
* Support for cycles in object graph - `(↻)` is displayed instead
38-
* Support for Hibernate lazy loaded entities - `⁂Lazy` is displayed instead
39-
40-
## vs. Lombok @ToString
41-
42-
Stringify Object for Java is designed for **slightly different** use case then Lombok.
43-
44-
Lombok `@ToString` is designed to quickly inspect fields of simple objects by generating static simple implementation of this mechanism.
45-
46-
Stringify Object for Java is designed to inspect complex objects that can have cycles and can be managed by JPA provider like Hibernate (introducing Lazy Loading problems).
47-
48-
#### Pros of Lombok vs Stringify Object
49-
50-
* Lombok is **fast** - it's statically generated code without using Reflection API.
51-
* Lombok is **easy** - it's zero configuration in most cases.
52-
53-
#### Cons of Lombok vs Stringify Object
54-
55-
* Lombok can't **detect cycles** is object graph, which implies `StackOverflowException` being thrown in that case
56-
* Lombok can't detect a **lazy loaded entities**, which leads to force loading it from JPA by invoking SQL statements. It's typical **n+1 problem**, but with nasty consequences - your `toString()` method is invoking SQL without your knowledge!!
57-
58-
## Requirements
59-
60-
* JDK >= 7
61-
* [EID Exceptions](https://github.com/wavesoftware/java-eid-exceptions)
62-
63-
### Contributing
64-
65-
Contributions are welcome!
66-
67-
To contribute, follow the standard [git flow](http://danielkummer.github.io/git-flow-cheatsheet/) of:
68-
69-
1. Fork it
70-
1. Create your feature branch (`git checkout -b feature/my-new-feature`)
71-
1. Commit your changes (`git commit -am 'Add some feature'`)
72-
1. Push to the branch (`git push origin feature/my-new-feature`)
73-
1. Create new Pull Request
74-
51+
52+
// inspect an object
53+
List<Person> people = query.getResultList();
54+
ObjectStringifier stringifier = new ObjectStringifier(people);
55+
stringifier.setMode(Mode.QUIET);
56+
assert "<Person id=15, parent=<Person id=16, parent=null, "
57+
+ "childs=[(↻)], account=⁂Lazy>, childs=[], "
58+
+ "account=⁂Lazy>".equals(stringifier.toString());
59+
```
60+
61+
## Features
62+
63+
* String representation of any Java class in two modes `PROMISCUOUS` and `QUIET`
64+
* Fine tuning of which fields to display
65+
* Support for cycles in object graph - `(↻)` is displayed instead
66+
* Support for Hibernate lazy loaded entities - `⁂Lazy` is displayed instead
67+
68+
## vs. Lombok @ToString
69+
70+
Stringify Object for Java is designed for **slightly different** use case then Lombok.
71+
72+
Lombok `@ToString` is designed to quickly inspect fields of simple objects by generating static simple implementation of this mechanism.
73+
74+
Stringify Object for Java is designed to inspect complex objects that can have cycles and can be managed by JPA provider like Hibernate (introducing Lazy Loading problems).
75+
76+
#### Pros of Lombok vs Stringify Object
77+
78+
* Lombok is **fast** - it's statically generated code without using Reflection API.
79+
* Lombok is **easy** - it's zero configuration in most cases.
80+
81+
#### Cons of Lombok vs Stringify Object
82+
83+
* Lombok can't **detect cycles** is object graph, which implies `StackOverflowException` being thrown in that case
84+
* Lombok can't detect a **lazy loaded entities**, which leads to force loading it from JPA by invoking SQL statements. It's typical **n+1 problem**, but with nasty consequences - your `toString()` method is invoking SQL without your knowledge!!
85+
86+
## Dependencies
87+
88+
* Java >= 7
89+
* [EID Exceptions](https://github.com/wavesoftware/java-eid-exceptions) library
90+
91+
### Contributing
92+
93+
Contributions are welcome!
94+
95+
To contribute, follow the standard [git flow](http://danielkummer.github.io/git-flow-cheatsheet/) of:
96+
97+
1. Fork it
98+
1. Create your feature branch (`git checkout -b feature/my-new-feature`)
99+
1. Commit your changes (`git commit -am 'Add some feature'`)
100+
1. Push to the branch (`git push origin feature/my-new-feature`)
101+
1. Create new Pull Request
102+
75103
Even if you can't contribute code, if you have an idea for an improvement please open an [issue](https://github.com/wavesoftware/java-stringify-object/issues).

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
<groupId>pl.wavesoftware.utils</groupId>
66
<artifactId>stringify-object</artifactId>
7-
<version>0.1.0</version>
7+
<version>1.0.0</version>
88
<packaging>jar</packaging>
99

1010
<name>Stringify Object for Java</name>
@@ -147,9 +147,9 @@
147147
<version>3.4.0.905</version>
148148
</plugin>
149149
<plugin>
150-
<groupId>external.atlassian.jgitflow</groupId>
150+
<groupId>com.manamind.jgitflow</groupId>
151151
<artifactId>jgitflow-maven-plugin</artifactId>
152-
<version>1.0-m5.1</version>
152+
<version>1.0.0</version>
153153
<configuration>
154154
<enableSshAgent>true</enableSshAgent>
155155
<flowInitContext>

src/main/java/pl/wavesoftware/utils/stringify/ObjectStringifier.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,40 @@
11
package pl.wavesoftware.utils.stringify;
22

33
import lombok.RequiredArgsConstructor;
4-
import pl.wavesoftware.utils.stringify.annotation.Inspect;
4+
import lombok.Setter;
5+
import pl.wavesoftware.utils.stringify.configuration.BeanFactory;
6+
import pl.wavesoftware.utils.stringify.configuration.Inspect;
7+
import pl.wavesoftware.utils.stringify.configuration.Mode;
58
import pl.wavesoftware.utils.stringify.impl.ToStringResolver;
9+
import pl.wavesoftware.utils.stringify.impl.ToStringResolverFactory;
10+
import pl.wavesoftware.utils.stringify.impl.ToStringResolverFactoryImpl;
611

712
/**
813
* @author <a href="krzysztof.suszynski@wavesoftware.pl">Krzysztof Suszyński</a>
914
* @since 2018-04-18
1015
*/
16+
@Setter
1117
@RequiredArgsConstructor
1218
public final class ObjectStringifier {
19+
private static final ToStringResolverFactory RESOLVER_FACTORY =
20+
new ToStringResolverFactoryImpl();
1321
private final Object target;
22+
private Mode mode = Mode.DEFAULT_MODE;
23+
private BeanFactory beanFactory;
24+
1425
/**
1526
* Creates string representation of given object using {@link Inspect} on given fields.
1627
*
1728
* @return a string representation of given object
1829
*/
1930
public CharSequence stringify() {
20-
return new ToStringResolver(target).resolve();
31+
ToStringResolver resolver = RESOLVER_FACTORY
32+
.create(target)
33+
.withMode(mode);
34+
if (beanFactory != null) {
35+
resolver = resolver.withBeanFactory(beanFactory);
36+
}
37+
return resolver.resolve();
2138
}
2239

2340
@Override

src/main/java/pl/wavesoftware/utils/stringify/annotation/Inspect.java

Lines changed: 0 additions & 21 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
import java.lang.reflect.Field;
4+
5+
/**
6+
* @author <a href="mailto:krzysztof.suszynski@coi.gov.pl">Krzysztof Suszynski</a>
7+
* @since 27.04.18
8+
*/
9+
public final class AlwaysTruePredicate implements Predicate<Field> {
10+
@Override
11+
public boolean test(Field field) {
12+
return true;
13+
}
14+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
/**
4+
* @author <a href="mailto:krzysztof.suszynski@coi.gov.pl">Krzysztof Suszynski</a>
5+
* @since 27.04.18
6+
*/
7+
public interface BeanFactory {
8+
<T> T create(Class<T> contractClass);
9+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
8+
/**
9+
* This annotation can be used to specify if given field should be displayed even
10+
* if it holds a null value.
11+
*
12+
* @author <a href="mailto:krzysztof.suszynski@coi.gov.pl">Krzysztof Suszynski</a>
13+
* @since 27.04.18
14+
*/
15+
@Retention(RetentionPolicy.RUNTIME)
16+
@Target(ElementType.FIELD)
17+
public @interface DisplayNull {
18+
19+
boolean BY_DEFAULT = false;
20+
21+
/**
22+
* Do show null value on field annotated with this annotation
23+
*
24+
* @return true if should display
25+
*/
26+
boolean value() default true;
27+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import java.lang.reflect.Field;
8+
9+
/**
10+
* When running in {@link Mode#PROMISCUOUS} this annotation can be used to exclude a
11+
* field from inspection.
12+
*
13+
* @author <a href="mailto:krzysztof.suszynski@coi.gov.pl">Krzysztof Suszynski</a>
14+
* @since 27.04.18
15+
*/
16+
@Retention(RetentionPolicy.RUNTIME)
17+
@Target(ElementType.FIELD)
18+
public @interface DoNotInspect {
19+
/**
20+
* If given this predicate will be used to conditionally check if annotated field should not
21+
* be inspected. This can lead to implementing conditional logic of field inspection.
22+
*
23+
* @return a class of predicate to be used to determine if field should not be inspected
24+
*/
25+
Class<? extends Predicate<Field>> conditionally() default AlwaysTruePredicate.class;
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
import java.lang.annotation.ElementType;
4+
import java.lang.annotation.Retention;
5+
import java.lang.annotation.RetentionPolicy;
6+
import java.lang.annotation.Target;
7+
import java.lang.reflect.Field;
8+
9+
/**
10+
* If {@link Mode} is set to {@link Mode#QUIET} (by default), this annotation
11+
* marks a field to be inspected to String representation of parent object.
12+
* <p>
13+
* If {@link Mode} is set to {@link Mode#PROMISCUOUS} this annotation has no function.
14+
*
15+
* @author <a href="krzysztof.suszynski@wavesoftware.pl">Krzysztof Suszyński</a>
16+
* @since 2018-04-18
17+
*/
18+
@Retention(RetentionPolicy.RUNTIME)
19+
@Target(ElementType.FIELD)
20+
public @interface Inspect {
21+
/**
22+
* If given this predicate will be used to conditionally check if annotated field should
23+
* be inspected. This can lead to implementing conditional logic of field inspection.
24+
*
25+
* @return a class of predicate to be used to determine if field should be inspected
26+
*/
27+
Class<? extends Predicate<Field>> conditionally() default AlwaysTruePredicate.class;
28+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package pl.wavesoftware.utils.stringify.configuration;
2+
3+
/**
4+
* A mode of operation. In {@link #PROMISCUOUS} mode every defined field is
5+
* automatically inspected unless the field is annotated with {@link DoNotInspect} annotation.
6+
* <p>
7+
* In {@link #QUIET} mode only fields annotated with {@link Inspect} will get inspected.
8+
*
9+
* @author <a href="mailto:krzysztof.suszynski@coi.gov.pl">Krzysztof Suszynski</a>
10+
* @since 27.04.18
11+
*/
12+
public enum Mode {
13+
PROMISCUOUS, QUIET;
14+
15+
public static final Mode DEFAULT_MODE = PROMISCUOUS;
16+
}

0 commit comments

Comments
 (0)