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    }