DataReprezDialectTools.java

package eu.javaexperience.datareprez;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import eu.javaexperience.collection.CollectionTools;
import eu.javaexperience.collection.map.BulkTransitMap;
import eu.javaexperience.collection.map.MapTools;
import eu.javaexperience.collection.map.NullMap;
import eu.javaexperience.datareprez.convertFrom.DataWrapper;
import eu.javaexperience.interfaces.simple.SimpleGetFactory;
import eu.javaexperience.reflect.CastTo;
import eu.javaexperience.reflect.Caster;
import eu.javaexperience.reflect.FieldNameDialect;
import eu.javaexperience.reflect.FieldNameDialects;
import eu.javaexperience.reflect.Mirror;
import eu.javaexperience.reflect.property.DataAccessor;

public class DataReprezDialectTools
{
	protected static final ConcurrentMap<Class, Map<String,Map<String, Field>>> DIALECT = new ConcurrentHashMap<>();
	
	protected static Map<String, Map<String, Field>> getDialect(Class cls)
	{
		Map<String, Map<String, Field>> ret = DIALECT.get(cls);
		
		if(null != ret && 0 == ret.size())
		{
			return ret;
		}
		
		Map<String, Map<String, Field>> dialects = new BulkTransitMap<>();
		
		for(Field f:Mirror.collectClassFields(cls, false))
		{
			ArrayList<FieldNameDialect> fnds = new ArrayList<>();
			
			CollectionTools.copyInto(f.getAnnotationsByType(FieldNameDialect.class), fnds);
			
			for(FieldNameDialects d:f.getAnnotationsByType(FieldNameDialects.class))
			{
				CollectionTools.copyInto(d.value(), fnds);
			}
			
			for(FieldNameDialect fnd:fnds)
			{
				String dial = fnd.dialect();
				String name = fnd.name();
				MapTools.ensureMapInMap(dialects, dial, SimpleGetFactory.getHashMapFactory()).put(name, f);
			}
		}
		
		if(0 == dialects.size())
		{
			dialects = NullMap.instance;
		}
		else
		{
			HashMap<String, Map<String, Field>> add = new HashMap<>();
			for(Entry<String, Map<String, Field>> kv:dialects.entrySet())
			{
				Map<String, Field> in = kv.getValue();
				if(in.size() > 0)
				{
					in = Collections.unmodifiableMap(in);
				}
				
				add.put(kv.getKey(), in);
			}
			dialects = Collections.unmodifiableMap(add);
		}
		
		DIALECT.put(cls, dialects);
		return dialects;
	}
	
	/**
	 * Dialect name => { fieldname => java field}
	 * */
	public static Map<String, Map<String, Field>> getDialectFieldsOfClass(Class<?> cls)
	{
		return getDialect(cls);
	}
	
	public static DataAccessor generateFieldDialectAccessor(final String dialect)
	{
		return new DataAccessor()
		{
			@Override
			public boolean canHandle(Object subject)
			{
				Class c = subject.getClass();
				if(c.isArray())
				{
					return false;
				}
				
				Map<String, Field> b = getDialect(c).get(dialect);
				
				return null!= b &&  0 != b.size();
			}
			
			@Override
			public String[] keys(Object subject)
			{
				return getDialect(subject.getClass()).get(dialect).keySet().toArray(Mirror.emptyStringArray);
			}
			
			@Override
			public Object get(Object subject, String key)
			{
				Field f = getDialect(subject.getClass()).get(dialect).get(key);
				if(null != f)
				{
					try
					{
						return f.get(subject);
					}
					catch (Exception e)
					{
						Mirror.propagateAnyway(e);
					}
				}
				
				return null;
			}
		};
	}
	
	public static boolean fillPoJoFromDialectNaive(Object target, String dialect, DataObject data)
	{
		Map<String, Field> fs = getDialectFieldsOfClass(target.getClass()).get(dialect);
		
		if(null == fs)
		{
			return false;
		}
		
		for(Entry<String, Field> kv:fs.entrySet())
		{
			Object o = data.opt(kv.getKey());
			if(null != o)
			{
				Field f = kv.getValue();
				Caster c = CastTo.getCasterRestrictlyForTargetClass(f.getType());
				if(null != c)
				{
					o = c.cast(o);
					if(null != o)
					{
						try
						{
							f.set(target, o);
						}
						catch (Exception e){}
					}
				}
			}
		}
		
		return true;
	}
	
	public static boolean fillPoJoFromDialectNaive(Object target, String dialect, Map<String, Object> data)
	{
		Map<String, Field> fs = getDialectFieldsOfClass(target.getClass()).get(dialect);
		
		if(null == fs)
		{
			return false;
		}
		
		for(Entry<String, Field> kv:fs.entrySet())
		{
			Object o = data.get(kv.getKey());
			if(null != o)
			{
				Field f = kv.getValue();
				Caster c = CastTo.getCasterRestrictlyForTargetClass(f.getType());
				if(null != c)
				{
					o = c.cast(o);
					if(null != o)
					{
						try
						{
							f.set(target, o);
						}
						catch (Exception e){}
					}
				}
			}
		}
		
		return true;
	}
	
	public static DataWrapper generateFieldDialectWrapper(final String dialect)
	{
		return new DataWrapper()
		{
			@Override
			public DataCommon wrap(DataWrapper topWrapper, DataCommon prototype, Object o)
			{
				Map<String, Map<String, Field>> d = getDialect(o.getClass());
				if(null == d || 0 == d.size())
				{
					return null;
				}
				
				Map<String, Field> di = d.get(dialect);
				if(null == di || 0 == di.size())
				{
					return null;
				}
				
				DataObject ret = prototype.newObjectInstance();
				
				for(Entry<String, Field> ent:di.entrySet())
				{
					String key = ent.getKey();

					try
					{
						Object w = ent.getValue().get(o);
						if(null != w)
						{
							if(null == DataReprezTools.put(topWrapper, ret, key, w))
							{
								DataReprezTools.put(topWrapper, ret, key, topWrapper.wrap(topWrapper, prototype, w));
							}
						}

					}
					catch (Exception e)
					{
						Mirror.propagateAnyway(e);
					}
				}
				
				return ret;
			}
		};
	}
}