package jeex.ejb.beans.mapping;

import jeex.ejb.HasId;

import javax.ejb.EJBException;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import java.lang.reflect.Field;

public class DomainMapper<T, E> {
   private final Class<T> domainClass;
   private final Class<E> entityClass;

   public DomainMapper(Class<T> domainClass, Class<E> entityClass) {
      this.domainClass = domainClass;
      this.entityClass = entityClass;
   }

   public T mapToDomain(E entity, T domainObj) {
      try {
         for (Field srcField : entityClass.getDeclaredFields()) {
            boolean useId = isRelation(srcField);

            for (Field targetField : domainClass.getDeclaredFields()) {
               if (useId) {
                  srcField.setAccessible(true);
                  Object value = srcField.get(entity);

                  if (value != null && targetField.getName().equals(srcField.getName() + "Id")) {
                     map(srcField, targetField, domainObj, ((HasId)value)::getId);
                     break;
                  }
               } else if (targetField.getName().equals(srcField.getName())) {
                  map(srcField, entity, targetField, domainObj);
                  break;
               }
            }
         }

         return domainObj;
      } catch (IllegalAccessException e) {
         // Should never happen
         throw new EJBException(e);
      }
   }

   public E mapToEntity(T domainObj, E entity) {
      for (Field targetField : entityClass.getDeclaredFields()) {
         // Skip field if relation or id
         if (isRelation(targetField) || isId(targetField)) {
            continue;
         }

         for (Field srcField : domainClass.getDeclaredFields()) {
            if (srcField.getName().equals(targetField.getName())) {
               map(srcField, domainObj, targetField, entity);
               break;
            }
         }
      }

      return entity;
   }

   private static boolean isId(Field f) {
      return f.isAnnotationPresent(Id.class);
   }

   private static boolean isRelation(Field f) {
      return (f.isAnnotationPresent(OneToOne.class)
            || f.isAnnotationPresent(OneToMany.class)
            || f.isAnnotationPresent(ManyToOne.class)
            || f.isAnnotationPresent(ManyToMany.class));
   }

   private static void map(Field srcField, Object src, Field targetField, Object target) {
      map(srcField, targetField, target, () -> srcField.get(src));
   }

   private static void map(Field srcField, Field targetField, Object target, ExceptionalSupplier<?> supplier) {
      try {
         srcField.setAccessible(true);
         targetField.setAccessible(true);
         targetField.set(target, supplier.get());
      } catch (Exception e) {
         throw new EJBException(e);
      }
   }

   @FunctionalInterface
   private interface ExceptionalSupplier<T> {
      T get() throws Exception;
   }
}

@@ -1048,38 +1048,28 @@
             
-map(srcField, 
+set(
 targetField,
@@ -1098,15 +1098,16 @@
 lue)
-::
+.
 getId
+()
 );%0A 
@@ -1561,24 +1561,39 @@
 E entity) %7B%0A
+      try %7B%0A   
       for (F
@@ -1638,32 +1638,35 @@
 aredFields()) %7B%0A
+   
          // Skip
@@ -1695,24 +1695,27 @@
 id%0A         
+   
 if (isRelati
@@ -1766,16 +1766,19 @@
         
+   
 continue
@@ -1779,16 +1779,19 @@
 ntinue;%0A
+   
         
@@ -1795,32 +1795,35 @@
     %7D%0A%0A         
+   
 for (Field srcFi
@@ -1871,24 +1871,27 @@
             
+   
 if (srcField
@@ -1926,32 +1926,35 @@
 d.getName())) %7B%0A
+   
                m
@@ -2006,32 +2006,35 @@
 %0A               
+   
 break;%0A         
@@ -2028,34 +2028,40 @@
 ak;%0A            
-%7D%0A
+   %7D%0A   
          %7D%0A     
@@ -2057,27 +2057,33 @@
     %7D%0A      
-%7D%0A%0A
+   %7D%0A%0A   
       return
@@ -2091,16 +2091,135 @@
 entity;%0A
+      %7D catch (IllegalAccessException e) %7B%0A         // Should never happen%0A         throw new EJBException(e);%0A      %7D%0A
    %7D%0A%0A  
@@ -2669,16 +2669,55 @@
  target)
+%0A         throws IllegalAccessException
  %7B%0A     
@@ -2717,28 +2717,24 @@
 %7B%0A      
-map(
 srcField
 , target
@@ -2721,26 +2721,56 @@
     srcField
-, 
+.setAccessible(true);%0A      set(
 targetField,
@@ -2781,14 +2781,8 @@
 get,
- () -%3E
  src
@@ -2831,164 +2831,102 @@
 oid 
-map(Field srcField, Field targetField, Object target, ExceptionalSupplier%3C?%3E supplier) %7B%0A      try %7B%0A         srcField.setAccessible(true);%0A         targetF
+set(Field field, Object target, Object value)%0A         throws IllegalAccessException %7B%0A      f
 ield
@@ -2957,18 +2957,9 @@
     
-   targetF
+f
 ield
@@ -2975,205 +2975,14 @@
 et, 
-supplier.get());%0A      %7D catch (Exception e) %7B%0A         throw new EJBException(e);%0A      %7D%0A   %7D%0A%0A   @FunctionalInterface%0A   private interface ExceptionalSupplier%3CT%3E %7B%0A      T get() throws Exception
+value)
 ;%0A  

Refactor DomainMapper