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 /**
29  * @author Vlad Mihalcea
30  */

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.classnew 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.classnew 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