From 221f5ad879cd639c85ce1d91dd4aa78efc6c1ac0 Mon Sep 17 00:00:00 2001
From: Robin Bygrave
Date: Thu, 21 Apr 2016 11:33:26 +1200
Subject: [PATCH] #651 - ENH: Add type support for java.nio.file.Path ... such
that it stores the underlying URI as VARCHAR/string
---
.../avaje/ebean/config/ClassLoadConfig.java | 7 ++++
.../server/type/DefaultTypeManager.java | 40 ++++++++++++++-----
.../server/type/ScalarTypePath.java | 40 +++++++++++++++++++
.../server/type/TypeManager.java | 4 +-
.../server/type/ScalarTypePathTest.java | 36 +++++++++++++++++
.../tests/model/types/SomeNewTypesBean.java | 11 +++++
.../com/avaje/tests/types/TestNewTypes.java | 8 ++++
7 files changed, 135 insertions(+), 11 deletions(-)
create mode 100644 src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePath.java
create mode 100644 src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypePathTest.java
diff --git a/src/main/java/com/avaje/ebean/config/ClassLoadConfig.java b/src/main/java/com/avaje/ebean/config/ClassLoadConfig.java
index 9c2342f08b..6519d1b050 100644
--- a/src/main/java/com/avaje/ebean/config/ClassLoadConfig.java
+++ b/src/main/java/com/avaje/ebean/config/ClassLoadConfig.java
@@ -33,6 +33,13 @@ public boolean isJavaTimePresent() {
return isPresent("java.time.LocalDate");
}
+ /**
+ * Return true if Java7 is present.
+ */
+ public boolean isJava7Present() {
+ return isPresent("java.nio.file.Path");
+ }
+
/**
* Return true if the Joda types are available and should be supported.
*/
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
index 168c41f05f..65b8e6e83d 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/DefaultTypeManager.java
@@ -125,6 +125,8 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
private final boolean objectMapperPresent;
+ private final boolean java7Present;
+
// OPTIONAL ScalarTypes registered if Jackson/JsonNode is in the classpath
/**
@@ -153,6 +155,7 @@ public final class DefaultTypeManager implements TypeManager, KnownImmutable {
*/
public DefaultTypeManager(ServerConfig config, BootupClasses bootupClasses) {
+ this.java7Present = config.getClassLoadConfig().isJava7Present();
this.jsonDateTime = config.getJsonDateTime();
this.checkImmutable = new CheckImmutable(this);
this.reflectScalarBuilder = new ReflectionBasedTypeBuilder(this);
@@ -282,19 +285,33 @@ public ScalarType> getScalarType(int jdbcType) {
/**
* This can return null if no matching ScalarType is found.
*/
- @SuppressWarnings("unchecked")
- public ScalarType getScalarType(Class type) {
- ScalarType found = (ScalarType) typeMap.get(type);
+ public ScalarType> getScalarType(Class> type) {
+ ScalarType> found = typeMap.get(type);
if (found == null) {
if (type.getName().equals("org.joda.time.LocalTime")) {
throw new IllegalStateException(
"ScalarType of Joda LocalTime not defined. You need to set ServerConfig.jodaLocalTimeMode to"
+ " either 'normal' or 'utc'. UTC is the old mode using UTC timezone but local time zone is now preferred as 'normal' mode.");
}
+ found = checkInterfaceTypes(type);
}
return found;
}
+ private ScalarType> checkInterfaceTypes(Class> type) {
+ if (java7Present) {
+ return checkJava7InterfaceTypes(type);
+ }
+ return null;
+ }
+
+ private ScalarType> checkJava7InterfaceTypes(Class> type) {
+ if (java.nio.file.Path.class.isAssignableFrom(type)) {
+ return typeMap.get(java.nio.file.Path.class);
+ }
+ return null;
+ }
+
public ScalarDataReader> getScalarDataReader(Class> propertyType, int sqlType) {
if (sqlType == 0) {
@@ -362,11 +379,11 @@ public ScalarType> getJsonScalarType(Class> type, int dbType) {
*
*/
@SuppressWarnings("unchecked")
- public ScalarType getScalarType(Class type, int jdbcType) {
+ public ScalarType> getScalarType(Class> type, int jdbcType) {
// File is a special Lob so check for that first
if (File.class.equals(type)) {
- return (ScalarType) fileType;
+ return fileType;
}
// check for Clob, LongVarchar etc ...
@@ -375,23 +392,23 @@ public ScalarType getScalarType(Class type, int jdbcType) {
ScalarType> scalarType = getLobTypes(jdbcType);
if (scalarType != null) {
// it is a specific Lob type...
- return (ScalarType) scalarType;
+ return scalarType;
}
scalarType = typeMap.get(type);
if (scalarType != null) {
if (jdbcType == 0 || scalarType.getJdbcType() == jdbcType) {
// matching type
- return (ScalarType) scalarType;
+ return scalarType;
}
}
// a util Date with jdbcType not matching server wide settings
if (type.equals(java.util.Date.class)) {
- return (ScalarType) extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
+ return extraTypeFactory.createUtilDate(jsonDateTime, jdbcType);
}
// a Calendar with jdbcType not matching server wide settings
if (type.equals(java.util.Calendar.class)) {
- return (ScalarType) extraTypeFactory.createCalendar(jsonDateTime, jdbcType);
+ return extraTypeFactory.createCalendar(jsonDateTime, jdbcType);
}
throw new IllegalArgumentException("Unmatched ScalarType for " + type + " jdbcType:" + jdbcType);
@@ -772,6 +789,11 @@ protected void initialiseJacksonTypes(ServerConfig config) {
}
protected void initialiseJavaTimeTypes(JsonConfig.DateTime mode, ServerConfig config) {
+
+ if (java7Present) {
+ typeMap.put(java.nio.file.Path.class, new ScalarTypePath());
+ }
+
if (config.getClassLoadConfig().isJavaTimePresent()) {
logger.debug("Registering java.time data types");
typeMap.put(java.time.LocalDate.class, new ScalarTypeLocalDate());
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePath.java b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePath.java
new file mode 100644
index 0000000000..56b13abdae
--- /dev/null
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/ScalarTypePath.java
@@ -0,0 +1,40 @@
+package com.avaje.ebeaninternal.server.type;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+/**
+ * ScalarType for java.nio.file.Path which converts to and from a VARCHAR database column.
+ */
+public class ScalarTypePath extends ScalarTypeBaseVarchar {
+
+ public ScalarTypePath() {
+ super(Path.class);
+ }
+
+ @Override
+ public Path convertFromDbString(String dbValue) {
+ try {
+ return Paths.get(new URI(dbValue));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Error with Path URI [" + dbValue + "] " + e);
+ }
+ }
+
+ @Override
+ public String convertToDbString(Path beanValue) {
+ return beanValue.toUri().toString();
+ }
+
+ @Override
+ public String formatValue(Path path) {
+ return convertToDbString(path);
+ }
+
+ @Override
+ public Path parse(String value) {
+ return convertFromDbString(value);
+ }
+}
diff --git a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
index 7ed5fa2619..56c2119d3c 100644
--- a/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
+++ b/src/main/java/com/avaje/ebeaninternal/server/type/TypeManager.java
@@ -42,14 +42,14 @@ public interface TypeManager {
/**
* Return the ScalarType for a given logical type.
*/
- ScalarType getScalarType(Class type);
+ ScalarType> getScalarType(Class> type);
/**
* For java.util.Date and java.util.Calendar additionally pass the jdbc type
* that you would like the ScalarType to map to. This is because these types
* can map to different java.sql.Types depending on the property.
*/
- ScalarType getScalarType(Class type, int jdbcType);
+ ScalarType> getScalarType(Class> type, int jdbcType);
/**
* Create a ScalarType for an Enum using a mapping (rather than JPA Ordinal
diff --git a/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypePathTest.java b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypePathTest.java
new file mode 100644
index 0000000000..641a82d736
--- /dev/null
+++ b/src/test/java/com/avaje/ebeaninternal/server/type/ScalarTypePathTest.java
@@ -0,0 +1,36 @@
+package com.avaje.ebeaninternal.server.type;
+
+import org.junit.Test;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static org.junit.Assert.assertEquals;
+
+public class ScalarTypePathTest {
+
+ private ScalarTypePath type = new ScalarTypePath();
+
+ @Test
+ public void convertFromDbString() throws Exception {
+
+ Path path = Paths.get(".");
+
+ String asString = type.convertToDbString(path);
+ Path converted = type.convertFromDbString(asString);
+
+ assertEquals(path, converted);
+ }
+
+ @Test
+ public void formatAndParse() throws Exception {
+
+ Path path = Paths.get(".");
+
+ String asString = type.formatValue(path);
+ Path converted = type.parse(asString);
+
+ assertEquals(path, converted);
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/java/com/avaje/tests/model/types/SomeNewTypesBean.java b/src/test/java/com/avaje/tests/model/types/SomeNewTypesBean.java
index 17a218c2e9..cc3015ddd2 100644
--- a/src/test/java/com/avaje/tests/model/types/SomeNewTypesBean.java
+++ b/src/test/java/com/avaje/tests/model/types/SomeNewTypesBean.java
@@ -4,6 +4,7 @@
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Version;
+import java.nio.file.Path;
import java.time.*;
@Entity
@@ -41,6 +42,8 @@ public class SomeNewTypesBean {
ZoneOffset zoneOffset;
+ Path path;
+
public Long getId() {
return id;
}
@@ -144,4 +147,12 @@ public ZoneOffset getZoneOffset() {
public void setZoneOffset(ZoneOffset zoneOffset) {
this.zoneOffset = zoneOffset;
}
+
+ public Path getPath() {
+ return path;
+ }
+
+ public void setPath(Path path) {
+ this.path = path;
+ }
}
diff --git a/src/test/java/com/avaje/tests/types/TestNewTypes.java b/src/test/java/com/avaje/tests/types/TestNewTypes.java
index bd807b1898..9955ed93e0 100644
--- a/src/test/java/com/avaje/tests/types/TestNewTypes.java
+++ b/src/test/java/com/avaje/tests/types/TestNewTypes.java
@@ -6,6 +6,7 @@
import org.junit.Test;
import java.io.IOException;
+import java.nio.file.Paths;
import java.time.*;
import java.util.List;
@@ -30,6 +31,7 @@ public void testInsertUpdate() throws IOException {
bean.setZoneId(ZoneId.systemDefault());
bean.setZoneOffset(ZonedDateTime.now().getOffset());
bean.setYearMonth(YearMonth.of(2014, 9));
+ bean.setPath(Paths.get("/tmp"));
Ebean.save(bean);
@@ -69,6 +71,9 @@ public void testInsertUpdate() throws IOException {
list = Ebean.find(SomeNewTypesBean.class).where().le("month", Month.SEPTEMBER).findList();
assertTrue(!list.isEmpty());
+ list = Ebean.find(SomeNewTypesBean.class).where().eq("path", Paths.get("/tmp")).findList();
+ assertTrue(!list.isEmpty());
+
SomeNewTypesBean fetched = Ebean.find(SomeNewTypesBean.class, bean.getId());
assertEquals(bean.getZoneId(), fetched.getZoneId());
@@ -80,6 +85,7 @@ public void testInsertUpdate() throws IOException {
assertEquals(bean.getLocalDateTime(), fetched.getLocalDateTime());
assertEquals(bean.getOffsetDateTime(), fetched.getOffsetDateTime());
assertEquals(bean.getInstant(), fetched.getInstant());
+ assertEquals(bean.getPath(), fetched.getPath());
String asJson = Ebean.json().toJson(fetched);
@@ -94,6 +100,7 @@ public void testInsertUpdate() throws IOException {
assertEquals(bean.getLocalDateTime(), toBean.getLocalDateTime());
assertEquals(bean.getOffsetDateTime(), toBean.getOffsetDateTime());
assertEquals(bean.getInstant(), toBean.getInstant());
+ assertEquals(bean.getPath(), toBean.getPath());
}
@@ -115,6 +122,7 @@ public void testInsertNull() {
assertNull(fetched.getLocalDateTime());
assertNull(fetched.getOffsetDateTime());
assertNull(fetched.getInstant());
+ assertNull(fetched.getPath());
}
}