001 /**
002 * GRANITE DATA SERVICES
003 * Copyright (C) 2006-2013 GRANITE DATA SERVICES S.A.S.
004 *
005 * This file is part of Granite Data Services.
006 *
007 * Granite Data Services is free software; you can redistribute it and/or modify
008 * it under the terms of the GNU Library General Public License as published by
009 * the Free Software Foundation; either version 2 of the License, or (at your
010 * option) any later version.
011 *
012 * Granite Data Services is distributed in the hope that it will be useful, but
013 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 * for more details.
016 *
017 * You should have received a copy of the GNU Library General Public License
018 * along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020 package org.granite.client.persistence;
021
022 import java.util.ArrayList;
023 import java.util.LinkedHashMap;
024 import java.util.List;
025 import java.util.Map;
026
027 import org.granite.client.persistence.collection.PersistentCollection;
028 import org.granite.messaging.reflect.Property;
029 import org.granite.messaging.reflect.PropertyAccessException;
030 import org.granite.messaging.reflect.PropertyNotFoundException;
031 import org.granite.messaging.reflect.Reflection;
032
033 /**
034 * @author Franck WOLFF
035 */
036 public class Persistence {
037
038 private static final String INITIALIZED_FIELD_NAME = "__initialized__";
039 private static final String DETACHED_STATE_FIELD_NAME = "__detachedState__";
040
041 private final Reflection reflection;
042
043 ///////////////////////////////////////////////////////////////////////////
044 // Constructor
045
046 public Persistence(Reflection reflection) {
047 this.reflection = reflection;
048 }
049
050 ///////////////////////////////////////////////////////////////////////////
051 // Entity
052
053 public boolean isEntity(Class<?> cls) {
054 return cls != null && cls.isAnnotationPresent(Entity.class);
055 }
056
057 protected void checkEntity(Class<?> entityClass) {
058 if (!isEntity(entityClass))
059 throw new PropertyNotFoundException("Not annotated with @" + Entity.class.getName() + ": " + entityClass);
060 }
061
062 ///////////////////////////////////////////////////////////////////////////
063 // Initialized
064
065 protected Property getInitializedProperty(Class<?> entityClass, boolean throwIfNotFound) {
066 checkEntity(entityClass);
067
068 Property property = reflection.findProperty(entityClass, INITIALIZED_FIELD_NAME, Boolean.TYPE);
069 if (property == null && throwIfNotFound)
070 throw new PropertyNotFoundException("No boolean " + INITIALIZED_FIELD_NAME + " property in " + entityClass);
071 return property;
072 }
073
074 public Property getInitializedProperty(Class<?> entityClass) {
075 return getInitializedProperty(entityClass, false);
076 }
077
078 public boolean hasInitializedProperty(Class<?> entityClass) {
079 return getInitializedProperty(entityClass, false) != null;
080 }
081
082 public boolean isInitialized(Object o) {
083 if (o instanceof PersistentCollection)
084 return ((PersistentCollection)o).wasInitialized();
085
086 Class<?> cls = o.getClass();
087 if (!isEntity(cls))
088 return true;
089
090 Property property = getInitializedProperty(cls, false);
091 if (property == null)
092 return true;
093
094 try {
095 return property.getBoolean(o);
096 }
097 catch (Exception e) {
098 throw new PropertyAccessException("Could not get " + property + " of object " + o, e);
099 }
100 }
101
102 public void setInitialized(Object entity, boolean value) {
103 Property property = getInitializedProperty(entity.getClass(), true);
104 try {
105 property.setBoolean(entity, value);
106 }
107 catch (Exception e) {
108 throw new PropertyAccessException("Could not set " + property + " of object " + entity + " to value " + value, e);
109 }
110 }
111
112 ///////////////////////////////////////////////////////////////////////////
113 // Detached state
114
115 protected Property getDetachedStateProperty(Class<?> entityClass, boolean throwIfNotFound) {
116 checkEntity(entityClass);
117
118 Property property = reflection.findProperty(entityClass, DETACHED_STATE_FIELD_NAME, String.class);
119 if (property == null && throwIfNotFound)
120 throw new PropertyNotFoundException("No String " + DETACHED_STATE_FIELD_NAME + " property in " + entityClass);
121 return property;
122 }
123
124 public Property getDetachedStateProperty(Class<?> entityClass) {
125 return getDetachedStateProperty(entityClass, false);
126 }
127
128 public boolean hasDetachedStateProperty(Class<?> entityClass) {
129 return getDetachedStateProperty(entityClass, false) != null;
130 }
131
132 public String getDetachedState(Object entity) {
133 Property property = getDetachedStateProperty(entity.getClass(), true);
134 try {
135 return (String)property.getObject(entity);
136 }
137 catch (Exception e) {
138 throw new PropertyAccessException("Could not get " + property + " of object " + entity, e);
139 }
140 }
141
142 public void setDetachedState(Object entity, String value) {
143 Property property = getDetachedStateProperty(entity.getClass(), true);
144 try {
145 property.setObject(entity, value);
146 }
147 catch (Exception e) {
148 throw new PropertyAccessException("Could not set " + property + " of object " + entity + " to value " + value, e);
149 }
150 }
151
152 ///////////////////////////////////////////////////////////////////////////
153 // Id
154
155 protected Property getIdProperty(Class<?> entityClass, boolean throwIfNotFound) {
156 checkEntity(entityClass);
157
158 Property property = reflection.findProperty(entityClass, Id.class);
159 if (property == null && throwIfNotFound)
160 throw new PropertyNotFoundException("No property annotated with " + Id.class.getName() + " in " + entityClass);
161 return property;
162 }
163
164 public Property getIdProperty(Class<?> entityClass) {
165 return getIdProperty(entityClass, false);
166 }
167
168 public boolean hasIdProperty(Class<?> entityClass) {
169 return getIdProperty(entityClass, false) != null;
170 }
171
172 @SuppressWarnings("unchecked")
173 public <T> T getId(Object entity) {
174 Property property = getIdProperty(entity.getClass(), true);
175 try {
176 return (T)property.getObject(entity);
177 }
178 catch (Exception e) {
179 throw new PropertyAccessException("Could not get " + property + " of object " + entity, e);
180 }
181 }
182
183 public void setId(Object entity, Object value) {
184 Property property = getIdProperty(entity.getClass(), true);
185 try {
186 property.setObject(entity, value);
187 }
188 catch (Exception e) {
189 throw new PropertyAccessException("Could not set " + property + " of object " + entity + " to value " + value, e);
190 }
191 }
192
193 ///////////////////////////////////////////////////////////////////////////
194 // Uid
195
196 protected Property getUidProperty(Class<?> entityClass, boolean throwIfNotFound) {
197 checkEntity(entityClass);
198
199 Property property = reflection.findProperty(entityClass, Uid.class);
200 if (property == null && throwIfNotFound)
201 throw new PropertyNotFoundException("No property annotated with " + Uid.class.getName() + " in " + entityClass);
202 return property;
203 }
204
205 public Property getUidProperty(Class<?> entityClass) {
206 return getUidProperty(entityClass, false);
207 }
208
209 public boolean hasUidProperty(Class<?> entityClass) {
210 return getUidProperty(entityClass, false) != null;
211 }
212
213 public String getUid(Object entity) {
214 Property property = getUidProperty(entity.getClass(), true);
215 try {
216 return (String)property.getObject(entity);
217 }
218 catch (Exception e) {
219 throw new PropertyAccessException("Could not get " + property + " of object " + entity, e);
220 }
221 }
222
223 public void setUid(Object entity, String value) {
224 Property property = getUidProperty(entity.getClass(), true);
225 try {
226 property.setObject(entity, value);
227 }
228 catch (Exception e) {
229 throw new PropertyAccessException("Could not set " + property + " of object " + entity + " to value " + value, e);
230 }
231 }
232
233 ///////////////////////////////////////////////////////////////////////////
234 // Version
235
236 protected Property getVersionProperty(Class<?> entityClass, boolean throwIfNotFound) {
237 checkEntity(entityClass);
238
239 Property property = reflection.findProperty(entityClass, Version.class);
240 if (property == null && throwIfNotFound)
241 throw new PropertyNotFoundException("No property annotated with " + Version.class.getName() + " in " + entityClass);
242 return property;
243 }
244
245 public Property getVersionProperty(Class<?> entityClass) {
246 return getVersionProperty(entityClass, false);
247 }
248
249 public boolean hasVersionProperty(Class<?> entityClass) {
250 return getVersionProperty(entityClass, false) != null;
251 }
252
253 @SuppressWarnings("unchecked")
254 public <T> T getVersion(Object entity) {
255 Property property = getVersionProperty(entity.getClass(), true);
256 try {
257 return (T)property.getObject(entity);
258 }
259 catch (Exception e) {
260 throw new PropertyAccessException("Could not get " + property + " of object " + entity, e);
261 }
262 }
263
264 public List<Property> getLazyProperties(Class<?> entityClass) {
265 List<Property> properties = reflection.findSerializableProperties(entityClass);
266 List<Property> lazyProperties = new ArrayList<Property>();
267 for (Property property : properties) {
268 if (property.isAnnotationPresent(Lazy.class))
269 lazyProperties.add(property);
270 }
271 return lazyProperties;
272 }
273
274
275 public List<Property> getProperties(Class<?> entityClass) {
276 return reflection.findSerializableProperties(entityClass);
277 }
278
279 public Object getPropertyValue(Object entity, String name, boolean raw) {
280 Property property = reflection.findSerializableProperty(entity.getClass(), name);
281 try {
282 return raw ? property.getRawObject(entity) : property.getObject(entity);
283 }
284 catch (Exception e) {
285 throw new RuntimeException("Could not get " + property + " of object " + entity);
286 }
287 }
288
289 public void setPropertyValue(Object entity, String name, Object value) {
290 Property property = reflection.findSerializableProperty(entity.getClass(), name);
291 try {
292 property.setObject(entity, value);
293 }
294 catch (Exception e) {
295 throw new RuntimeException("Could not set " + property + " of object " + entity);
296 }
297 }
298
299 public Map<String, Object> getPropertyValues(Object entity, boolean raw, boolean excludeIdUid, boolean excludeVersion, boolean includeReadOnly) {
300 Map<String, Object> values = new LinkedHashMap<String, Object>();
301
302 List<Property> properties = reflection.findSerializableProperties(entity.getClass());
303
304 List<Property> excludedProperties = new ArrayList<Property>();
305 if (isEntity(entity.getClass())) {
306 excludedProperties.add(getInitializedProperty(entity.getClass()));
307 excludedProperties.add(getDetachedStateProperty(entity.getClass()));
308 if (excludeIdUid && hasIdProperty(entity.getClass()))
309 excludedProperties.add(getIdProperty(entity.getClass()));
310 if (excludeIdUid && hasUidProperty(entity.getClass()))
311 excludedProperties.add(getUidProperty(entity.getClass()));
312 if (excludeVersion && hasVersionProperty(entity.getClass()))
313 excludedProperties.add(getVersionProperty(entity.getClass()));
314 }
315
316 for (Property property : properties) {
317 if (excludedProperties.contains(property))
318 continue;
319
320 if (!includeReadOnly && !property.isWritable())
321 continue;
322
323 try {
324 values.put(property.getName(), raw ? property.getRawObject(entity) : property.getObject(entity));
325 }
326 catch (Exception e) {
327 throw new RuntimeException("Could not get property " + property.getName() + " on entity " + entity);
328 }
329 }
330 return values;
331 }
332 }