001    /*
002      GRANITE DATA SERVICES
003      Copyright (C) 2012 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    
021    package org.granite.client.tide.data.impl;
022    
023    import java.lang.annotation.ElementType;
024    import java.lang.reflect.Field;
025    import java.lang.reflect.Method;
026    import java.util.HashMap;
027    import java.util.Map;
028    
029    import javax.validation.Path;
030    import javax.validation.Path.Node;
031    import javax.validation.TraversableResolver;
032    
033    import org.granite.client.tide.data.Dirty;
034    import org.granite.client.tide.data.Id;
035    import org.granite.client.tide.data.Lazy;
036    import org.granite.client.tide.data.Version;
037    import org.granite.client.tide.data.spi.DataManager;
038    import org.granite.client.tide.data.spi.EntityDescriptor;
039    import org.granite.messaging.amf.RemoteClass;
040    import org.granite.util.Introspector;
041    
042    /**
043     * @author William DRAI
044     */
045    public abstract class AbstractDataManager implements DataManager {
046    
047        private static Map<Class<?>, EntityDescriptor> entityDescriptors = new HashMap<Class<?>, EntityDescriptor>(50);
048        
049        
050        public EntityDescriptor getEntityDescriptor(Object entity) {
051            EntityDescriptor desc = entityDescriptors.get(entity.getClass());
052            if (desc != null)
053                    return desc;
054                    
055            String className;
056            if (entity.getClass().isAnnotationPresent(RemoteClass.class))
057                className = entity.getClass().getAnnotation(RemoteClass.class).value();
058            else
059                className = entity.getClass().getName();
060            
061            String idPropertyName = null, versionPropertyName = null, dirtyPropertyName = null;
062            @SuppressWarnings("unused")
063                    boolean hasDirty = false;
064            Field dsField = null, initField = null;
065            
066            Map<String, Boolean> lazy = new HashMap<String, Boolean>();
067            for (Method m : entity.getClass().getMethods()) {
068                if (m.getName().startsWith("get") && m.getParameterTypes().length == 0 && m.getReturnType() != Void.class) {
069                    if (m.isAnnotationPresent(Id.class))
070                        idPropertyName = Introspector.decapitalize(m.getName().substring(3));
071                    else if (m.isAnnotationPresent(Version.class))
072                        versionPropertyName = Introspector.decapitalize(m.getName().substring(3));
073                    else if (m.isAnnotationPresent(Lazy.class))
074                        lazy.put(Introspector.decapitalize(m.getName().substring(3)), true);
075                }
076                else if (m.getName().startsWith("is") && m.getParameterTypes().length == 0 && m.getReturnType() == boolean.class) {
077                    if (m.isAnnotationPresent(Dirty.class))
078                            dirtyPropertyName = Introspector.decapitalize(m.getName().substring(2));
079                }
080            }
081            
082            Class<?> clazz = entity.getClass();
083            while (clazz != null && clazz != Object.class) {
084                for (Field f : clazz.getDeclaredFields()) {
085                    if (f.isAnnotationPresent(Id.class) && idPropertyName == null) {
086                        idPropertyName = f.getName();
087                    }
088                    else if (f.isAnnotationPresent(Version.class) && versionPropertyName == null) {
089                        versionPropertyName = f.getName();
090                    }
091                    else if (f.isAnnotationPresent(Dirty.class) && dirtyPropertyName == null) {
092                        dirtyPropertyName = f.getName();
093                    }
094                    else if (f.getName().equals("__detachedState") && dsField == null)
095                        dsField = f;
096                    else if (f.getName().equals("__initialized") && initField == null)
097                        initField = f;
098                    else if (f.isAnnotationPresent(Lazy.class))
099                        lazy.put(Introspector.decapitalize(f.getName().substring(3)), true);
100                }
101                clazz = clazz.getSuperclass();
102            }
103            
104            desc = new EntityDescriptor(className, idPropertyName, versionPropertyName, dirtyPropertyName, dsField, initField, lazy);
105            entityDescriptors.put(entity.getClass(), desc);
106    
107            return desc;
108        }
109        
110        
111        private TraversableResolver traversableResolver = new TraversableResolverImpl();
112        
113        public TraversableResolver getTraversableResolver() {
114            return traversableResolver;
115        }
116        
117        public class TraversableResolverImpl implements TraversableResolver {
118            
119            public boolean isReachable(Object bean, Node propertyPath, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
120                    return true;
121            }
122            
123            public boolean isCascadable(Object bean, Node propertyPath, Class<?> rootBeanType, Path pathToTraversableObject, ElementType elementType) {
124                    return true;
125            }
126    
127        }
128    }