All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.granite.tide.data.DataMergeContext Maven / Gradle / Ivy

The newest version!
/**
 *   GRANITE DATA SERVICES
 *   Copyright (C) 2006-2014 GRANITE DATA SERVICES S.A.S.
 *
 *   This file is part of the Granite Data Services Platform.
 *
 *   Granite Data Services is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Lesser General Public
 *   License as published by the Free Software Foundation; either
 *   version 2.1 of the License, or (at your option) any later version.
 *
 *   Granite Data Services is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 *   General Public License for more details.
 *
 *   You should have received a copy of the GNU Lesser General Public
 *   License along with this library; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
 *   USA, or see .
 */
package org.granite.tide.data;

import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.granite.config.ConvertersConfig;
import org.granite.context.GraniteContext;
import org.granite.messaging.amf.io.util.ClassGetter;
import org.granite.tide.IUID;


/**
 * @author William DRAI
 */
public class DataMergeContext {
    
    private static ThreadLocal dataMergeContext = new ThreadLocal() {
        @Override
        protected DataMergeContext initialValue() {
            return new DataMergeContext();
        }
    };
    
    public static DataMergeContext get() {
        return dataMergeContext.get();
    }
    
    public static void remove() {
    	dataMergeContext.remove();
    }
    
    private final Map cache = new HashMap();
    private final Map loadedEntities = new HashMap();


    public static Map getCache() {
    	return dataMergeContext.get().cache;
    }
    
    public static void addLoadedEntity(Object entity) {
    	DataMergeContext mergeContext = dataMergeContext.get();
    	mergeContext.entityLoaded(entity);
    }
    
    public static void addResultEntity(Object result) {
    	DataMergeContext mergeContext = dataMergeContext.get();
    	ClassGetter classGetter = ((ConvertersConfig)GraniteContext.getCurrentInstance().getGraniteConfig()).getClassGetter();
    	mergeContext.addResultEntity(result, classGetter, new HashSet());
    }
    
    @SuppressWarnings("unchecked")
    public void addResultEntity(Object result, ClassGetter classGetter, Set cache) {
    	if (result == null || cache.contains(result))
    		return;
    	
    	cache.add(result);
    	
    	if (classGetter.isEntity(result))
    		addLoadedEntity(result);
    	
    	List values = classGetter.getFieldValues(result, result);
    	for (Object[] value : values) {
    		Object val = value[1];
    		if (val == null || val.getClass().isPrimitive())
    			continue;
        	if (!classGetter.isInitialized(result, ((Field)value[0]).getName(), val))
        		continue;
        		
    		if (val.getClass().isArray()) {
    			for (int i = 0; i < Array.getLength(val); i++)
    				addResultEntity(Array.get(val, i), classGetter, cache);
    		}
    		else if (val instanceof Collection) {
    			for (Iterator i = ((Collection)val).iterator(); i.hasNext(); )
    				addResultEntity(i.next(), classGetter, cache);
    		}
    		else if (val instanceof Map) {
    			for (Iterator> i = ((Map)val).entrySet().iterator(); i.hasNext(); ) {
    				Map.Entry me = i.next();
    				addResultEntity(me.getKey(), classGetter, cache);
    				addResultEntity(me.getValue(), classGetter, cache);
    			}
    		}
    		else
    			addResultEntity(val, classGetter, cache);
    	}
    }
    
    private void entityLoaded(Object entity) {
    	Object key = CacheKey.key(entity, null, null);
    	Object cachedEntity = cache.get(key);
    	if (cachedEntity != null) { // && cachedEntity != entity) {
    		// Entity has already been merged before, replace current merge context by the one from JPA
    		cache.clear();
    	}
    	
    	if (entity instanceof IUID)
    		loadedEntities.put(entity.getClass().getName() + "-" + ((IUID)entity).getUid(), entity);
    	else
    		loadedEntities.put(entity, entity);
    }
    
    public static Object getLoadedEntity(Object entity) {
    	if (entity instanceof IUID)
    		return dataMergeContext.get().loadedEntities.get(entity.getClass().getName() + "-" + ((IUID)entity).getUid());
    	return dataMergeContext.get().loadedEntities.get(entity);
    }
    
    public static Collection getLoadedEntities() {
    	return dataMergeContext.get().loadedEntities.values();
    }
    
    public static void restoreLoadedEntities(Set loadedEntities) {
    	DataMergeContext mergeContext = dataMergeContext.get();
    	for (Object entity : loadedEntities) {
    		if (entity instanceof IUID)
    			mergeContext.loadedEntities.put(entity.getClass().getName() + "-" + ((IUID)entity).getUid(), entity);
    		else
    			mergeContext.loadedEntities.put(entity, entity);
    	}
    }
    
    
    public static class CacheKey {
        
        public static Object key(Object obj, Object owner, String propertyName) {
            if (obj instanceof IUID)
            	return new UIDKey(obj.getClass(), ((IUID)obj).getUid());
            if (owner != null && (obj instanceof Collection || obj instanceof Map))
                return new CollectionKey(owner, propertyName);
            return obj;
        }
    }
    
    public static class UIDKey extends CacheKey {
    	
    	private Class clazz;
    	private String uid;
    	
    	public UIDKey(Class clazz, String uid) {
    		this.clazz = clazz;
    		this.uid = uid;
    	}
        
        @Override
        public boolean equals(Object obj) {
            if (obj == null || !obj.getClass().equals(UIDKey.class))
                return false;
            return this.clazz.equals(((UIDKey)obj).clazz) && this.uid.equals(((UIDKey)obj).uid);
        }
        
        @Override
        public int hashCode() {
            return (clazz.getName() + "-" + uid).hashCode();
        }
    }
    
    public static class CollectionKey extends CacheKey {
        
        private Object owner;
        private String propertyName;
        
        public CollectionKey(Object owner, String propertyName) {
            this.owner = owner;
            this.propertyName = propertyName;
        }
        
        @Override
        public boolean equals(Object obj) {
            if (obj == null || !obj.getClass().equals(CollectionKey.class))
                return false;
            return this.owner.equals(((CollectionKey)obj).owner) && this.propertyName.equals(((CollectionKey)obj).propertyName);
        }
        
        @Override
        public int hashCode() {
            return owner.hashCode()*31 + propertyName.hashCode();
        }
    }
}