1 package org.jcr_blog.jcrmapping;
2
3 import com.google.common.collect.Lists;
4 import com.google.common.collect.Sets;
5 import java.lang.reflect.Field;
6 import java.lang.reflect.ParameterizedType;
7 import java.lang.reflect.Type;
8 import java.util.ArrayList;
9 import java.util.Collection;
10 import java.util.List;
11 import java.util.Set;
12 import javax.enterprise.context.ApplicationScoped;
13 import javax.enterprise.inject.Any;
14 import javax.enterprise.inject.Instance;
15 import javax.inject.Inject;
16 import javax.inject.Named;
17 import javax.jcr.Node;
18 import javax.jcr.Property;
19 import javax.jcr.RepositoryException;
20 import javax.jcr.Value;
21 import javax.jcr.ValueFactory;
22 import javax.validation.constraints.NotNull;
23 import org.jcr_blog.jcrmapping.converter.CollectionConverter;
24 import org.jcr_blog.jcrmapping.converter.Converter;
25 import org.slf4j.Logger;
26 import scala.collection.Iterator;
27
28
29
30
31
32
33
34 @Named()
35 @ApplicationScoped
36 public class NodeConverterService {
37
38 @Inject
39 private Logger logger;
40 @Inject
41 @Any
42 private Instance<Converter> converters;
43 @Inject
44 @Any
45 private Instance<CollectionConverter> collectionConverters;
46
47 public <E> E nodeToEntity(Node n, Class<E> clazz) throws NodeConverterException {
48 try {
49 E result = clazz.newInstance();
50 Class objOrSuper = clazz;
51 while (objOrSuper != null) {
52 for (Field field : objOrSuper.getDeclaredFields()) {
53 final PropertyConfiguration propertyConfiguration;
54 if ((propertyConfiguration = field.getAnnotation(PropertyConfiguration.class)) == null) {
55 continue;
56 }
57
58 if (propertyConfiguration.special() == PropertyConfiguration.SpecialProperty.name) {
59 field.setAccessible(true);
60 field.set(result, createObject(field.getType(), n.getSession().getValueFactory().createValue(n.getName())));
61 continue;
62 }
63
64 if (propertyConfiguration.special() == PropertyConfiguration.SpecialProperty.path) {
65 field.setAccessible(true);
66 field.set(result, createObject(field.getType(), n.getSession().getValueFactory().createValue(n.getPath())));
67 continue;
68 }
69
70 String name = propertyConfiguration.name();
71 if (name.isEmpty()) {
72 name = field.getName();
73 }
74
75 if (!n.hasProperty(name)) {
76 logger.debug("node does not have a property by the name of {}. skipping.", name);
77 continue;
78 }
79
80
81 final Property property = n.getProperty(name);
82
83 if (property.isMultiple()) {
84 Type genericFieldType = field.getGenericType();
85
86 if (genericFieldType instanceof ParameterizedType) {
87 ParameterizedType aType = (ParameterizedType) genericFieldType;
88 Type[] fieldArgTypes = aType.getActualTypeArguments();
89 if (fieldArgTypes.length != 1) {
90
91 throw new UnsupportedOperationException("Currently only one generic parameter is supported.");
92 }
93 final Class fieldArgClass = (Class) fieldArgTypes[0];
94 Value[] values = property.getValues();
95
96 Object objects = null;
97
98 for (CollectionConverter converter : collectionConverters) {
99 if (converter.isApplicableValuesToObjects(field.getType())) {
100 objects = converter.toObjects(values, new CollectionConverter.CreateObjectCallback() {
101 @Override
102 public Object toObject(Value value) throws RepositoryException, NodeConverterException {
103 return createObject(fieldArgClass, value);
104 }
105 });
106 break;
107 }
108 }
109
110 if(objects == null) {
111 throw new UnsupportedOperationException(String.format("Currently no collection converter applicable to class %s is registered.", field.getType()));
112 }
113
114 field.setAccessible(true);
115 field.set(result, objects);
116 } else {
117 throw new UnsupportedOperationException("Collections must be defined using parameterized types.");
118 }
119 } else {
120 Value v = property.getValue();
121 field.setAccessible(true);
122 field.set(result, createObject(field.getType(), v));
123 }
124 }
125
126 objOrSuper = objOrSuper.getSuperclass();
127 }
128
129 return result;
130 } catch (final Exception ex) {
131 throw new NodeConverterException(ex);
132 }
133 }
134
135
136
137
138
139
140
141
142
143
144 private <E> E createObject(Class<E> clazz, Value value) throws RepositoryException, NodeConverterException {
145
146 for (Converter c : converters) {
147 if (c.isApplicableValueToObject(clazz)) {
148 return (E) c.toObject(value);
149 }
150 }
151
152 throw new UnsupportedOperationException(String.format("valueToObject - Currently no converter applicable to class %s is registered.", clazz));
153 }
154
155
156
157
158
159
160
161
162
163
164 public Value createValue(Object object, ValueFactory valueFactory) throws RepositoryException, NodeConverterException {
165 if (object == null) {
166 return null;
167 }
168
169 for (Converter c : converters) {
170 if (c.isApplicableObjectToValue(object)) {
171 return c.toValue(object, valueFactory);
172 }
173 }
174
175 throw new UnsupportedOperationException(String.format("objectToValue - Currently no converter applicable to class %s is registered.", object.getClass()));
176 }
177
178 public <E> Node getOrCreateNode(@NotNull final E entity, final Node parent) throws NodeConverterException {
179 try {
180 Class objOrSuper = entity.getClass();
181 while (objOrSuper != null) {
182 for (Field field : objOrSuper.getDeclaredFields()) {
183 final PropertyConfiguration propertyConfiguration;
184 if ((propertyConfiguration = field.getAnnotation(PropertyConfiguration.class)) == null) {
185 continue;
186 }
187
188 if (propertyConfiguration.special() == PropertyConfiguration.SpecialProperty.name) {
189 field.setAccessible(true);
190 Object fieldValue = field.get(entity);
191 if(fieldValue == null) {
192 throw new NullPointerException("Field annotated with PropertyConfiguration.SpecialProperty.name must not be null");
193 }
194 String name = createValue(fieldValue, parent.getSession().getValueFactory()).toString();
195 if (parent.hasNode(name)) {
196 return parent.getNode(name);
197 } else {
198 return parent.addNode(name);
199 }
200 }
201
202 }
203 objOrSuper = objOrSuper.getSuperclass();
204 }
205 } catch (final Exception ex) {
206 throw new NodeConverterException(ex);
207 }
208 throw new IllegalArgumentException("No entity field has been annotated with the SpecialProperty.name");
209 }
210
211 public <E> void entityToNode(@NotNull final E entity, @NotNull final Node node) throws NodeConverterException {
212 try {
213 ValueFactory valueFactory = node.getSession().getValueFactory();
214
215 Class objOrSuper = entity.getClass();
216 while (objOrSuper != null) {
217 for (Field field : objOrSuper.getDeclaredFields()) {
218 final PropertyConfiguration propertyConfiguration;
219 if ((propertyConfiguration = field.getAnnotation(PropertyConfiguration.class)) == null) {
220 continue;
221 }
222
223
224 switch (propertyConfiguration.special()) {
225 case name:
226 case path:
227 continue;
228 default:
229 break;
230 }
231
232 String name = propertyConfiguration.name();
233 if (name.isEmpty()) {
234 name = field.getName();
235 }
236
237 field.setAccessible(true);
238 Object object = field.get(entity);
239 if (object == null) {
240 if (node.hasProperty(name)) {
241 node.getProperty(name).remove();
242 }
243 } else {
244
245
246 if (object instanceof Collection) {
247 Collection objects = (Collection) object;
248 List<Value> values = Lists.newArrayListWithCapacity(objects.size());
249 for (final Object o : objects) {
250 values.add(createValue(o, valueFactory));
251 }
252 node.setProperty(name, values.toArray(new Value[values.size()]));
253 } else if (object instanceof scala.collection.Iterable) {
254 Iterator iterator = ((scala.collection.Iterable) object).toIterator();
255 List<Value> values = Lists.newArrayListWithCapacity(iterator.size());
256 while (iterator.hasNext()) {
257 values.add(createValue(iterator.next(), valueFactory));
258 }
259 node.setProperty(name, values.toArray(new Value[values.size()]));
260 } else {
261 node.setProperty(name, createValue(object, valueFactory));
262 }
263 }
264
265 }
266 objOrSuper = objOrSuper.getSuperclass();
267 }
268 } catch (final Exception ex) {
269 throw new NodeConverterException(ex);
270 }
271 }
272 }