GenericStorageMappingData.java

package hu.ddsi.java.database;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import eu.javaexperience.reflect.Mirror;
import hu.ddsi.java.database.fieldAnnotations.GenericStoreIgnore;
import hu.ddsi.java.database.fieldAnnotations.GenericStoreNative;
import hu.ddsi.java.database.fieldAnnotations.GenericStoreSerialized;
import hu.ddsi.java.database.fieldAnnotations.GenericStoreStorableID;

public class GenericStorageMappingData
{
	public final Class cls;
	public final FieldData[] allFd;
	public final FieldData[] storedFd;
	
	protected GenericStorageMappingData(Class cls, FieldData[] fds)
	{
		this.cls = cls;
		this.allFd = fds;
		
		ArrayList<FieldData> fs = new ArrayList<>();
		for(FieldData fd: fds)
		{
			if(fd.isStored())
			{
				fs.add(fd);
			}
		}
		
		this.storedFd = fs.toArray(emptyFDA);
	}
	
	public static final FieldData[] emptyFDA = new FieldData[0];
	
	protected static Map<Class<? extends GenericStorable>, GenericStorageMappingData> classesFieldDatas = new ConcurrentHashMap<>();
	static
	{
		classesFieldDatas.put(GenericStorable.class, new GenericStorageMappingData(GenericStorable.class, emptyFDA));
	}
	
	protected static Map<String,Class<?>> stringToClass = new HashMap<>();
	
	protected static boolean isNeedToStore(Field f)
	{
		return !(Modifier.isStatic(f.getModifiers()) || dontStoreByAnnotation(f));
	}

	protected static boolean dontStoreByAnnotation(Field f)
	{
		//TeaVm might return null
		Annotation[] anns = f.getAnnotations();
		
		if(null != anns)
		{
			for(Annotation a:anns)
			{
				if(a instanceof GenericStoreIgnore)
					return true;
				else if(a instanceof GenericStoreNative)
					return false;
				else if(a instanceof GenericStoreStorableID)
					return false;
				else if(a instanceof GenericStoreSerialized)
					return false;
			}
		}
		return false;
	}
	
	public static GenericStorageMappingData getOrCollectAllClassData(Class<? extends GenericStorable> cls)
	{
		GenericStorageMappingData ret = classesFieldDatas.get(cls);
		if(ret != null)
		{
			return ret;
		}
		
		GenericStorable instance = null;
		
		if(!cls.isInterface() && !Modifier.isAbstract(cls.getModifiers()))
		{
			try
			{
				instance = GenericStorage.newInstance(cls);
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
		
		return getOrCollectClassData(cls, instance);
	}
	
	protected static FieldData[] getOrCollectClassData(Class cls) throws InstantiationException, IllegalAccessException
	{
		return getOrCollectAllClassData(cls).storedFd;
	}
	
	protected static GenericStorageMappingData getOrCollectClassData(Class cls, GenericStorable obj)
	{
		if(null != obj)
		{
			cls = obj.getClass();
		}

		GenericStorageMappingData data = classesFieldDatas.get(cls);
		
		if(data != null)
		{
			return data;
		}
		
		ArrayList<FieldData> fds = new ArrayList<>();
		Map<String,GenericStoreMode> map = null;
		
		if(null != obj)
		{
			map = obj.getSelfDefinedMapping();
		}
		
		ArrayList<Field> tmp = new ArrayList<>();
		Mirror.collectClassFields(cls, tmp, false);

		
		if(map != null)
		{
			GenericStoreMode def = map.get(null);
			if(def == null)
				def = GenericStoreMode.GenericStoreDont;
				
			for(Field f:tmp)
			{
				if(Modifier.isStatic(f.getModifiers()))
				{
					continue;
				}
				
				GenericStoreMode mod = map.get(f.getName());
				
				if(!isNeedToStore(f))
				{
					mod = GenericStoreMode.GenericStoreDont;
				}
				
				if(mod == null)
					mod = def;
				
				FieldData fd = new FieldData(f,mod);
				if(fd.getDataType() == null)
					continue;
				
				addOverwrite(fd, fds);
			}
		}
		else
		{
			for(Field f:tmp)
			{
				if(Modifier.isStatic(f.getModifiers()))
				{
					continue;
				}
				
				FieldData fd = new FieldData(f);
				if(fd.getDataType() == null)
					continue;
				
				addOverwrite(fd, fds);
			}
		}
		
		data = new GenericStorageMappingData(cls, fds.toArray(emptyFDA));
		
		classesFieldDatas.put(cls,data);
		stringToClass.put(cls.getName(), cls);
		
		return data;
	}
	
	protected static void addOverwrite(FieldData data, ArrayList<FieldData> fds)
	{
		String name = data.f.getName();
		for(int i=0;i<fds.size();++i)
		{
			FieldData d = fds.get(i);
			if(d.f.getName().equals(name))
			{
				//fds.set(i, data);
				return;
			}			
		}
		
		fds.add(data);
	}
}