Skip to content

Commit

Permalink
https://github.com/javers/javers/issues/1350
Browse files Browse the repository at this point in the history
added PathTypeAdapter for java.nio.file.Path
  • Loading branch information
bartoszwalacik committed Dec 31, 2023
1 parent b263ea8 commit 67afb5a
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 24 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.javers.common.collections;

import java.io.File;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.zone.ZoneRules;
import java.util.TimeZone;
Expand All @@ -21,7 +23,7 @@ public class WellKnownValueTypes {
BigDecimal.class,
BigInteger.class,
ThreadLocal.class,
UUID.class,
//UUID.class,
Currency.class,
URI.class,
URL.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package org.javers.core.json;

public interface AbstractJsonTypeAdapter {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package org.javers.core.json;

import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;

import java.lang.reflect.Type;

public abstract class BasicStringSuperTypeAdapter<T> implements JsonAdvancedTypeAdapter<T> {

/**
* Example serialization for LocalDateTime:
* <pre>
* public String serialize(LocalDateTime sourceValue) {
* return ISO_DATE_TIME_FORMATTER.print(sourceValue);
* }
* </pre>
* @param sourceValue not null
*/
public abstract String serialize(T sourceValue);

/**
* Example deserialization for LocalDateTime:
* <pre>
* public LocalDateTime deserialize(String serializedValue) {
* return ISO_DATE_TIME_FORMATTER.parseLocalDateTime(serializedValue);
* }
* </pre>
*
* @param serializedValue not null
*/
public abstract T deserialize(String serializedValue);

@Override
public T fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext jsonDeserializationContext ) {
return deserialize(json.getAsJsonPrimitive().getAsString());
}

@Override
public JsonElement toJson(T sourceValue, Type typeOfT, JsonSerializationContext context) {
return new JsonPrimitive(serialize(sourceValue));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*
* @author bartosz.walacik
*/
public interface JsonAdvancedTypeAdapter<T> {
public interface JsonAdvancedTypeAdapter<T> extends AbstractJsonTypeAdapter {

T fromJson(JsonElement json, Type typeOfT, JsonDeserializationContext context);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public JsonConverterBuilder() {
this.gsonBuilder.setExclusionStrategies(new SkipFieldExclusionStrategy());
this.gsonBuilder.setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE);
registerBuiltInAdapters(Java8TypeAdapters.adapters());
registerBuiltInAdapters((List)UtilTypeCoreAdapters.adapters());
registerBuiltInAdapters(UtilTypeCoreAdapters.adapters());
registerJsonAdvancedTypeAdapter(new OptionalTypeAdapter());
}

Expand Down Expand Up @@ -143,6 +143,14 @@ public JsonConverterBuilder registerJsonAdvancedTypeAdapter(JsonAdvancedTypeAdap
return this;
}

private void registerJsonTypeAdapterForType(Class targetType, final JsonTypeAdapter adapter) {
JsonSerializer jsonSerializer = (value, type, jsonSerializationContext) -> adapter.toJson(value, jsonSerializationContext);
JsonDeserializer jsonDeserializer = (jsonElement, type, jsonDeserializationContext) -> adapter.fromJson(jsonElement, jsonDeserializationContext);

registerNativeGsonSerializer(targetType, jsonSerializer);
registerNativeGsonDeserializer(targetType, jsonDeserializer);
}

public JsonConverter build() {
registerBuiltInAdapter(new AtomicTypeAdapter(typeSafeValues));

Expand All @@ -158,20 +166,19 @@ public JsonConverter build() {
return new JsonConverter(gsonBuilder.create());
}

private void registerJsonTypeAdapterForType(Class targetType, final JsonTypeAdapter adapter) {
JsonSerializer jsonSerializer = (value, type, jsonSerializationContext) -> adapter.toJson(value, jsonSerializationContext);
JsonDeserializer jsonDeserializer = (jsonElement, type, jsonDeserializationContext) -> adapter.fromJson(jsonElement, jsonDeserializationContext);

registerNativeGsonSerializer(targetType, jsonSerializer);
registerNativeGsonDeserializer(targetType, jsonDeserializer);
}

private void registerBuiltInAdapters(final List<JsonTypeAdapter> adapters) {
adapters.forEach(this::registerBuiltInAdapter);
private <T extends AbstractJsonTypeAdapter> void registerBuiltInAdapters(final List<T> adapters) {
adapters.forEach(a -> {
if (a instanceof JsonAdvancedTypeAdapter) {
registerJsonAdvancedTypeAdapter((JsonAdvancedTypeAdapter) a);
}
if (a instanceof JsonTypeAdapter) {
registerBuiltInAdapter((JsonTypeAdapter) a);
}
});
}

private void registerBuiltInAdapter(final JsonTypeAdapter adapter) {
adapter.getValueTypes().forEach( c -> registerJsonTypeAdapterForType((Class) c, adapter));
adapter.getValueTypes().forEach(c -> registerJsonTypeAdapterForType((Class) c, adapter));
}

private static class SkipFieldExclusionStrategy implements ExclusionStrategy {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
* @see JsonAdvancedTypeAdapter
* @author bartosz walacik
*/
public interface JsonTypeAdapter<T> {
public interface JsonTypeAdapter<T> extends AbstractJsonTypeAdapter {

/**
* @param json not null and not JsonNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.javers.core.json.typeadapter.util;

import org.javers.core.json.BasicStringSuperTypeAdapter;
import java.nio.file.Path;

public class PathTypeAdapter extends BasicStringSuperTypeAdapter<Path> {

@Override
public String serialize(Path sourceValue) {
return sourceValue.toString();
}

@Override
public Path deserialize(String serializedValue) {
return Path.of(serializedValue);
}

@Override
public Class<Path> getTypeSuperclass() {
return Path.class;
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package org.javers.core.json.typeadapter.util;

import org.javers.common.collections.Lists;
import org.javers.common.exception.JaversException;
import org.javers.common.exception.JaversExceptionCode;
import org.javers.core.json.AbstractJsonTypeAdapter;
import org.javers.core.json.BasicStringTypeAdapter;
import org.javers.core.json.JsonAdvancedTypeAdapter;
import org.javers.core.json.JsonTypeAdapter;
import org.javers.core.metamodel.type.ValueType;
import org.javers.java8support.OptionalTypeAdapter;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -72,21 +78,33 @@ public static Date toUtilDate(LocalDateTime localDateTime) {
return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
}

public static List<BasicStringTypeAdapter> adapters() {
return (List) Lists.immutableListOf(
public static List<AbstractJsonTypeAdapter> adapters() {
return Lists.immutableListOf(
new PathTypeAdapter(),
new JavaUtilDateTypeAdapter(),
new JavaSqlDateTypeAdapter(),
new JavaSqlTimestampTypeAdapter(),
new JavaSqlTimeTypeAdapter(),
new FileTypeAdapter(),
new UUIDTypeAdapter()
);
);
}

public static List<ValueType> valueTypes() {
return adapters()
.stream()
.map(c -> new ValueType(c.getValueType()))
.flatMap(it -> getValueTypes(it).stream())
.map(c -> new ValueType(c))
.collect(Collectors.toList());
}

private static List<Class<?>> getValueTypes(AbstractJsonTypeAdapter adapter) {
if (adapter instanceof JsonAdvancedTypeAdapter) {
return List.of( ((JsonAdvancedTypeAdapter) adapter).getTypeSuperclass());
}
if (adapter instanceof JsonTypeAdapter) {
return ((JsonTypeAdapter) adapter).getValueTypes();
}
throw new JaversException(JaversExceptionCode.NOT_IMPLEMENTED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import spock.lang.Specification
/**
* @author bartosz.walacik
*/
public class OpBoxCaseClassExtractionError extends Specification {
class OpBoxCaseClassExtractionError extends Specification {
interface ParamType {
String getName()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import org.javers.core.JaversBuilder
import org.javers.core.metamodel.annotation.Id
import spock.lang.Specification

import java.nio.file.Path

/**
* case https://github.com/javers/javers/issues/1350
*/
class PathInaccessibleObjectExceptionTest extends Specification {

class Entity {
@Id int id
Path path
}

def "should support java.nio.file.Path" () {
given:
def javers = JaversBuilder.javers().build()

when:
def diff = javers.compare(
new Entity(id:1, path: Path.of("foo")),
new Entity(id:1, path: Path.of("bar")))

then:
println(diff)
diff.changes.size() == 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll

import java.nio.file.Path

/**
* @author bartosz.walacik
*/
Expand All @@ -17,23 +19,25 @@ class JsonConverterUtilTypesTest extends Specification{
@Unroll
def "should convert #expectedType (#givenValue) to and from JSON"(){
expect:
javers.getTypeMapping(expectedType) instanceof ValueType
javers.jsonConverter.toJson( givenValue ) == expectedJson
javers.jsonConverter.fromJson( expectedJson, expectedType ) == givenValue
javers.getTypeMapping(expectedType) instanceof ValueType

where:
expectedType << [UUID, Currency, URI, URL, File]
expectedType << [UUID, Currency, URI, URL, File, Path]
givenValue << [new UUID(123456,654321),
Currency.getInstance("PLN"),
new URI("http://example.com"),
new URL("http://example.com"),
new File("/tmp/file.txt")
new File("/tmp/file.txt"),
Path.of("file")
]
expectedJson << ['"00000000-0001-e240-0000-00000009fbf1"',
'"PLN"',
'"http://example.com"',
'"http://example.com"',
JsonOutput.toJson(new File('/tmp/file.txt').toString())
JsonOutput.toJson(new File('/tmp/file.txt').toString()),
'"file"'
]
}
}

0 comments on commit 67afb5a

Please sign in to comment.