Details | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1878 | jmachado | 1 | package com.owlike.genson.reflect; |
2 | |||
3 | import java.util.ArrayList; |
||
4 | import java.util.Arrays; |
||
5 | import java.util.Collections; |
||
6 | import java.util.Comparator; |
||
7 | import java.util.LinkedHashMap; |
||
8 | import java.util.List; |
||
9 | import java.util.Map; |
||
10 | |||
11 | import com.owlike.genson.*; |
||
12 | import com.owlike.genson.reflect.BeanCreator.BeanCreatorProperty; |
||
13 | import com.owlike.genson.stream.ObjectReader; |
||
14 | import com.owlike.genson.stream.ObjectWriter; |
||
15 | |||
16 | /** |
||
17 | * BeanDescriptors are used to serialize/deserialize objects based on their fields, methods and |
||
18 | * constructors. By default it is supposed to work on JavaBeans, however it can be configured and |
||
19 | * extended to support different kind of objects. |
||
20 | * <p/> |
||
21 | * In most cases BeanDescriptors should not be used directly as it is used internally to support |
||
22 | * objects not handled by the default Converters. The most frequent case when you will use directly |
||
23 | * a BeanDescriptor is when you want to deserialize into an existing instance. Here is an example : |
||
24 | * <p/> |
||
25 | * <pre> |
||
26 | * Genson genson = new Genson(); |
||
27 | * BeanDescriptorProvider provider = genson.getBeanDescriptorProvider(); |
||
28 | * BeanDescriptor<MyClass> descriptor = provider.provide(MyClass.class, genson); |
||
29 | * |
||
30 | * MyClass existingInstance = descriptor.deserialize(existingInstance, new JsonReader("{}"), |
||
31 | * new Context(genson)); |
||
32 | * </pre> |
||
33 | * |
||
34 | * @param <T> type that this BeanDescriptor can serialize and deserialize. |
||
35 | * @author eugen |
||
36 | * @see BeanDescriptorProvider |
||
37 | */ |
||
38 | public class BeanDescriptor<T> implements Converter<T> { |
||
39 | final Class<?> fromDeclaringClass; |
||
40 | final Class<T> ofClass; |
||
41 | final Map<String, PropertyMutator> mutableProperties; |
||
42 | final List<PropertyAccessor> accessibleProperties; |
||
43 | final boolean failOnMissingProperty; |
||
44 | |||
45 | final BeanCreator creator; |
||
46 | private final boolean _noArgCtr; |
||
47 | |||
48 | private static final 5+0%2Fdocs%2Fapi+Object">Object MISSING = new 5+0%2Fdocs%2Fapi+Object">Object(); |
||
49 | // Used as a cache so we just copy it instead of recreating and assigning the default values |
||
50 | private 5+0%2Fdocs%2Fapi+Object">Object[] globalCreatorArgs; |
||
51 | |||
52 | private final static Comparator<BeanProperty> _readablePropsComparator = new Comparator<BeanProperty>() { |
||
53 | public int compare(BeanProperty o1, BeanProperty o2) { |
||
54 | return o1.name.compareToIgnoreCase(o2.name); |
||
55 | } |
||
56 | }; |
||
57 | |||
58 | public 1.5.0/docs/api/java/beans/BeanDescriptor.html">BeanDescriptor(Class<T> forClass, Class<?> fromDeclaringClass, |
||
59 | List<PropertyAccessor> readableBps, |
||
60 | Map<String, PropertyMutator> writableBps, BeanCreator creator, |
||
61 | boolean failOnMissingProperty) { |
||
62 | this.ofClass = forClass; |
||
63 | this.fromDeclaringClass = fromDeclaringClass; |
||
64 | this.creator = creator; |
||
65 | this.failOnMissingProperty = failOnMissingProperty; |
||
66 | mutableProperties = writableBps; |
||
67 | |||
68 | 1.5.0/docs/api/java/util/Collections.html">Collections.sort(readableBps, _readablePropsComparator); |
||
69 | |||
70 | accessibleProperties = 1.5.0/docs/api/java/util/Collections.html">Collections.unmodifiableList(readableBps); |
||
71 | if (this.creator != null) { |
||
72 | _noArgCtr = this.creator.parameters.size() == 0; |
||
73 | globalCreatorArgs = new 5+0%2Fdocs%2Fapi+Object">Object[creator.parameters.size()]; |
||
74 | 1.5.0/docs/api/java/util/Arrays.html">Arrays.fill(globalCreatorArgs, MISSING); |
||
75 | } else { |
||
76 | _noArgCtr = false; |
||
77 | } |
||
78 | } |
||
79 | |||
80 | public boolean isReadable() { |
||
81 | return !accessibleProperties.isEmpty(); |
||
82 | } |
||
83 | |||
84 | public boolean isWritable() { |
||
85 | return creator != null; |
||
86 | } |
||
87 | |||
88 | public void serialize(T obj, ObjectWriter writer, 5+0%2Fdocs%2Fapi+Context">Context ctx) { |
||
89 | writer.beginObject(); |
||
90 | RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
||
91 | for (PropertyAccessor accessor : accessibleProperties) { |
||
92 | if (runtimePropertyFilter.shouldInclude(accessor, ctx)) accessor.serialize(obj, writer, ctx); |
||
93 | } |
||
94 | writer.endObject(); |
||
95 | } |
||
96 | |||
97 | public T deserialize(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) { |
||
98 | T bean = null; |
||
99 | // optimization for default ctr |
||
100 | if (_noArgCtr) { |
||
101 | bean = ofClass.cast(creator.create()); |
||
102 | deserialize(bean, reader, ctx); |
||
103 | } else { |
||
104 | if (creator == null) |
||
105 | throw new JsonBindingException("No constructor has been found for type " |
||
106 | + ofClass); |
||
107 | bean = _deserWithCtrArgs(reader, ctx); |
||
108 | } |
||
109 | return bean; |
||
110 | } |
||
111 | |||
112 | public void deserialize(T into, ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) { |
||
113 | reader.beginObject(); |
||
114 | RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
||
115 | for (; reader.hasNext(); ) { |
||
116 | reader.next(); |
||
117 | 1.5.0/docs/api/java/lang/String.html">String propName = reader.name(); |
||
118 | PropertyMutator mutator = mutableProperties.get(propName); |
||
119 | if (mutator != null) { |
||
120 | if (runtimePropertyFilter.shouldInclude(mutator, ctx)) { |
||
121 | mutator.deserialize(into, reader, ctx); |
||
122 | } else { |
||
123 | reader.skipValue(); |
||
124 | } |
||
125 | } else if (failOnMissingProperty) throw missingPropertyException(propName); |
||
126 | else reader.skipValue(); |
||
127 | } |
||
128 | reader.endObject(); |
||
129 | } |
||
130 | |||
131 | |||
132 | protected T _deserWithCtrArgs(ObjectReader reader, 5+0%2Fdocs%2Fapi+Context">Context ctx) { |
||
133 | List<String> names = new ArrayList<String>(); |
||
134 | List<Object> values = new ArrayList<Object>(); |
||
135 | RuntimePropertyFilter runtimePropertyFilter = ctx.genson.runtimePropertyFilter(); |
||
136 | |||
137 | reader.beginObject(); |
||
138 | for (; reader.hasNext(); ) { |
||
139 | reader.next(); |
||
140 | 1.5.0/docs/api/java/lang/String.html">String propName = reader.name(); |
||
141 | PropertyMutator muta = mutableProperties.get(propName); |
||
142 | |||
143 | if (muta != null) { |
||
144 | if (runtimePropertyFilter.shouldInclude(muta, ctx)) { |
||
145 | 5+0%2Fdocs%2Fapi+Object">Object param = muta.deserialize(reader, ctx); |
||
146 | names.add(propName); |
||
147 | values.add(param); |
||
148 | } else { |
||
149 | reader.skipValue(); |
||
150 | } |
||
151 | } else if (failOnMissingProperty) throw missingPropertyException(propName); |
||
152 | else reader.skipValue(); |
||
153 | } |
||
154 | |||
155 | int size = names.size(); |
||
156 | int foundCtrParameters = 0; |
||
157 | 5+0%2Fdocs%2Fapi+Object">Object[] creatorArgs = globalCreatorArgs.clone(); |
||
158 | 1.5.0/docs/api/java/lang/String.html">String[] newNames = new 1.5.0/docs/api/java/lang/String.html">String[size]; |
||
159 | 5+0%2Fdocs%2Fapi+Object">Object[] newValues = new 5+0%2Fdocs%2Fapi+Object">Object[size]; |
||
160 | |||
161 | for (int i = 0, j = 0; i < size; i++) { |
||
162 | BeanCreatorProperty mp = creator.paramsAndAliases.get(names.get(i)); |
||
163 | if (mp != null) { |
||
164 | creatorArgs[mp.index] = values.get(i); |
||
165 | foundCtrParameters++; |
||
166 | } else { |
||
167 | newNames[j] = names.get(i); |
||
168 | newValues[j] = values.get(i); |
||
169 | j++; |
||
170 | } |
||
171 | } |
||
172 | |||
173 | if (foundCtrParameters < creator.parameters.size()) updateWithDefaultValues(creatorArgs, ctx.genson); |
||
174 | |||
175 | T bean = ofClass.cast(creator.create(creatorArgs)); |
||
176 | for (int i = 0; i < size; i++) { |
||
177 | PropertyMutator property = mutableProperties.get(newNames[i]); |
||
178 | if (property != null) property.mutate(bean, newValues[i]); |
||
179 | } |
||
180 | reader.endObject(); |
||
181 | return bean; |
||
182 | } |
||
183 | |||
184 | private void updateWithDefaultValues(5+0%2Fdocs%2Fapi+Object">Object[] creatorArgs, Genson genson) { |
||
185 | for (int i = 0; i < creatorArgs.length; i++) { |
||
186 | if (creatorArgs[i] == MISSING) { |
||
187 | for (BeanCreatorProperty property : creator.parameters.values()) { |
||
188 | if (property.index == i) { |
||
189 | creatorArgs[i] = genson.defaultValue(property.getRawClass()); |
||
190 | break; |
||
191 | } |
||
192 | } |
||
193 | } |
||
194 | } |
||
195 | } |
||
196 | |||
197 | public Class<T> getOfClass() { |
||
198 | return ofClass; |
||
199 | } |
||
200 | |||
201 | private JsonBindingException missingPropertyException(1.5.0/docs/api/java/lang/String.html">String name) { |
||
202 | return new JsonBindingException("No matching property in " + getOfClass() + " for key " + name); |
||
203 | } |
||
204 | } |