1 package com.vladmihalcea.hibernate.type.json.internal;
2
3 import com.vladmihalcea.hibernate.type.util.ObjectMapperWrapper;
4 import com.vladmihalcea.hibernate.type.util.ReflectionUtils;
5 import org.hibernate.HibernateException;
6 import org.hibernate.annotations.common.reflection.XProperty;
7 import org.hibernate.annotations.common.reflection.java.JavaXMember;
8 import org.hibernate.engine.jdbc.BinaryStream;
9 import org.hibernate.engine.jdbc.internal.BinaryStreamImpl;
10 import org.hibernate.type.descriptor.WrapperOptions;
11 import org.hibernate.type.descriptor.java.AbstractTypeDescriptor;
12 import org.hibernate.type.descriptor.java.BlobTypeDescriptor;
13 import org.hibernate.type.descriptor.java.DataHelper;
14 import org.hibernate.type.descriptor.java.MutableMutabilityPlan;
15 import org.hibernate.usertype.DynamicParameterizedType;
16
17 import java.io.ByteArrayInputStream;
18 import java.io.IOException;
19 import java.io.InputStream;
20 import java.lang.reflect.ParameterizedType;
21 import java.lang.reflect.Type;
22 import java.sql.Blob;
23 import java.sql.SQLException;
24 import java.util.Collection;
25 import java.util.Objects;
26 import java.util.Properties;
27
28
31 public class JsonTypeDescriptor
32 extends AbstractTypeDescriptor<Object> implements DynamicParameterizedType {
33
34 private Type type;
35
36 private ObjectMapperWrapper objectMapperWrapper;
37
38 public JsonTypeDescriptor() {
39 super(Object.class, new MutableMutabilityPlan<Object>() {
40 @Override
41 protected Object deepCopyNotNull(Object value) {
42 return ObjectMapperWrapper.INSTANCE.clone(value);
43 }
44 });
45 }
46
47 public JsonTypeDescriptor(Type type) {
48 this();
49 this.type = type;
50 }
51
52 public JsonTypeDescriptor(final ObjectMapperWrapper objectMapperWrapper) {
53 super(Object.class, new MutableMutabilityPlan<Object>() {
54 @Override
55 protected Object deepCopyNotNull(Object value) {
56 return objectMapperWrapper.clone(value);
57 }
58 });
59 this.objectMapperWrapper = objectMapperWrapper;
60 }
61
62 public JsonTypeDescriptor(final ObjectMapperWrapper objectMapperWrapper, Type type) {
63 this(objectMapperWrapper);
64 this.type = type;
65 }
66
67 @Override
68 public void setParameterValues(Properties parameters) {
69 final XProperty xProperty = (XProperty) parameters.get(DynamicParameterizedType.XPROPERTY);
70 if (xProperty instanceof JavaXMember) {
71 type = ReflectionUtils.invokeGetter(xProperty, "javaType");
72 } else {
73 type = ((ParameterType) parameters.get(PARAMETER_TYPE)).getReturnedClass();
74 }
75 }
76
77 @Override
78 public boolean areEqual(Object one, Object another) {
79 if (one == another) {
80 return true;
81 }
82 if (one == null || another == null) {
83 return false;
84 }
85 if (one instanceof String && another instanceof String) {
86 return one.equals(another);
87 }
88 if (one instanceof Collection && another instanceof Collection) {
89 return Objects.equals(one, another);
90 }
91 return objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(one)).equals(
92 objectMapperWrapper.toJsonNode(objectMapperWrapper.toString(another))
93 );
94 }
95
96 @Override
97 public String toString(Object value) {
98 return objectMapperWrapper.toString(value);
99 }
100
101 @Override
102 public Object fromString(String string) {
103 if (String.class.isAssignableFrom(typeToClass())) {
104 return string;
105 }
106 return objectMapperWrapper.fromString(string, type);
107 }
108
109 @SuppressWarnings({"unchecked"})
110 @Override
111 public <X> X unwrap(Object value, Class<X> type, WrapperOptions options) {
112 if (value == null) {
113 return null;
114 }
115
116 if (String.class.isAssignableFrom(type)) {
117 return value instanceof String ? (X) value : (X) toString(value);
118 } else if (BinaryStream.class.isAssignableFrom(type) ||
119 byte[].class.isAssignableFrom(type)) {
120 String stringValue = (value instanceof String) ? (String) value : toString(value);
121
122 return (X) new BinaryStreamImpl(DataHelper.extractBytes(new ByteArrayInputStream(stringValue.getBytes())));
123 } else if (Blob.class.isAssignableFrom(type)) {
124 String stringValue = (value instanceof String) ? (String) value : toString(value);
125
126 final Blob blob = BlobTypeDescriptor.INSTANCE.fromString(stringValue);
127 return (X) blob;
128 } else if (Object.class.isAssignableFrom(type)) {
129 String stringValue = (value instanceof String) ? (String) value : toString(value);
130 return (X) objectMapperWrapper.toJsonNode(stringValue);
131 }
132
133 throw unknownUnwrap(type);
134 }
135
136 @Override
137 public <X> Object wrap(X value, WrapperOptions options) {
138 if (value == null) {
139 return null;
140 }
141
142 Blob blob = null;
143
144 if (Blob.class.isAssignableFrom(value.getClass())) {
145 blob = options.getLobCreator().wrap((Blob) value);
146 } else if (byte[].class.isAssignableFrom(value.getClass())) {
147 blob = options.getLobCreator().createBlob((byte[]) value);
148 } else if (InputStream.class.isAssignableFrom(value.getClass())) {
149 InputStream inputStream = (InputStream) value;
150 try {
151 blob = options.getLobCreator().createBlob(inputStream, inputStream.available());
152 } catch (IOException e) {
153 throw unknownWrap(value.getClass());
154 }
155 }
156
157 String stringValue;
158 try {
159 stringValue = (blob != null) ? new String(DataHelper.extractBytes(blob.getBinaryStream())) : value.toString();
160 } catch (SQLException e) {
161 throw new HibernateException("Unable to extract binary stream from Blob", e);
162 }
163
164 return fromString(stringValue);
165 }
166
167 private Class typeToClass() {
168 Type classType = type;
169 if (type instanceof ParameterizedType) {
170 classType = ((ParameterizedType) type).getRawType();
171 }
172 return (Class) classType;
173 }
174 }
175