package no.niths.common.helpers; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collection; import java.util.List; import javax.xml.bind.annotation.XmlTransient; import no.niths.common.misc.Accessor; import no.niths.domain.Domain; import org.codehaus.jackson.annotate.JsonIgnore; /** * Class used too clear or fetch relations so the lazy * loading works correctly * */ public class LazyFixer<T> { private final Object[] varargsNull = new Object[] { null }; /** * Sets any children in the list of domains to null * * @param list * the list of which the relations are to be cleared */ public void clearRelations(List<T> list) { try { for (Object domain : list) { removeChild(domain, domain.getClass()); } } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { e.printStackTrace(); } } /** * Sets any 2nd level children to null * * @param domain * the domain of which 2nd level children are to be removed */ @SuppressWarnings("unchecked") public void clearSubRelations(T domain) { Class<?> domainType = domain.getClass(); outer: for (Field outerField : domainType.getDeclaredFields()) { Class<?> outerType = outerField.getType(); String outerFieldName = outerField.getName(); try { // The current domain's (collection) child will yield true if (Collection.class.isAssignableFrom(outerType)) { // If the annotations indicate they are transient, skip to // the next attribute if (checkAnnotations( outerField.getDeclaredAnnotations())) { continue outer; } // Find the collection's getter method Method outerMethod = domainType .getMethod( generateAccessorHeader(outerFieldName, Accessor.GET), (Class<?>[]) null); Object result = outerMethod.invoke(domain); if (result != null) { for (Domain innerCollection : ( Collection<Domain>) result) { removeChild(innerCollection, innerCollection.getClass()); } } } else if (Domain.class.isAssignableFrom(outerType)) { if (checkAnnotations(outerField.getAnnotations())) { // Nullify domain that are transient domainType.getDeclaredMethod( generateAccessorHeader(outerFieldName, Accessor.SET), outerType).invoke( domain, varargsNull); // Nullify all domains and collections in the domain } else { Method m = domainType.getMethod( generateAccessorHeader(outerFieldName, Accessor.GET), (Class<?>[]) null); Domain result = (Domain) m.invoke(domain); if (result != null) { removeChild(result, result.getClass() .getSuperclass()); } } } } catch (Exception e) { e.printStackTrace(); } } } @SuppressWarnings("unchecked") public void fetchChildren(List<T> list) { for (Object element : list) { if (element == null) { return; } Class<?> type0 = element.getClass(); try { for (Field field : element.getClass().getDeclaredFields()) { Class<?> type = field.getType(); if (!checkAnnotations(field.getAnnotations()) && (Collection.class.isAssignableFrom(type) || Domain.class .isAssignableFrom(type))) { Method m = type0.getMethod( generateAccessorHeader(field.getName(), Accessor.GET), (Class<?>[]) null); Object result = m.invoke(element); if (result != null) { Class<?> resultClass = result.getClass(); if (Collection.class.isAssignableFrom( resultClass)) { Collection<Domain> domains = (Collection<Domain>) result; domains.size(); } else if (Domain.class .isAssignableFrom(resultClass)) { Domain domain = (Domain) result; domain.getId(); } } } } } catch (Exception e) { e.printStackTrace(); } } } /** * * @param annotations * the field's annotations * @return whether the annotations indicate that the field is transient or * not */ private boolean checkAnnotations(Annotation[] annotations) { boolean isTransient = false; for (Annotation annotation : annotations) { Class<?> annotationType = annotation.annotationType(); if (annotationType == JsonIgnore.class || annotationType == XmlTransient.class) { isTransient = true; } } return isTransient; } private String generateAccessorHeader(String fieldName, Accessor accessor) { return String.format( "%s%s", accessor, Character.toUpperCase(fieldName.charAt(0)) + fieldName.substring(1)); } /** * * @param target * the object to be set to null * @param type * the class * @throws NoSuchMethodException * @throws SecurityException * @throws IllegalAccessException * @throws IllegalArgumentException * @throws InvocationTargetException */ private void removeChild(Object target, Class<?> type) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { for (Field field : type.getDeclaredFields()) { Class<?> fieldType = field.getType(); // Nullify any domain or collection if (Collection.class.isAssignableFrom(fieldType) || Domain.class.isAssignableFrom(fieldType)) { Method m = type.getMethod( generateAccessorHeader(field.getName(), Accessor.SET), fieldType); m.invoke(target, varargsNull); } } } }