br.gov.frameworkdemoiselle.ldap.internal.ClazzUtils.java Source code

Java tutorial

Introduction

Here is the source code for br.gov.frameworkdemoiselle.ldap.internal.ClazzUtils.java

Source

/**
 * Copyright (c) 2012 - Reinaldo de Carvalho <reinaldoc@gmail.com>
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *  
 * This program 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 General Public License for more details. 
 * 
 */

package br.gov.frameworkdemoiselle.ldap.internal;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.ArrayUtils;
import org.slf4j.Logger;

import br.gov.frameworkdemoiselle.annotation.Ignore;
import br.gov.frameworkdemoiselle.annotation.Name;
import br.gov.frameworkdemoiselle.internal.producer.LoggerProducer;
import br.gov.frameworkdemoiselle.ldap.annotation.DistinguishedName;
import br.gov.frameworkdemoiselle.ldap.annotation.Id;
import br.gov.frameworkdemoiselle.ldap.annotation.LDAPEntry;
import br.gov.frameworkdemoiselle.ldap.annotation.ParentDN;
import br.gov.frameworkdemoiselle.ldap.annotation.RemoveOnMerge;
import br.gov.frameworkdemoiselle.ldap.core.EntryManager;
import br.gov.frameworkdemoiselle.ldap.exception.EntryException;
import br.gov.frameworkdemoiselle.util.Beans;
import br.gov.frameworkdemoiselle.util.Reflections;
import br.gov.frameworkdemoiselle.util.contrib.Strings;

public class ClazzUtils {

    private static Logger logger = LoggerProducer.create(ClazzUtils.class);

    public static final boolean EntryObjectListCascade = false;

    public static final boolean EntryObjectCascade = true;

    /**
     * Get Distinguished Name value of a @LDAPEntry annotated object. The
     * Distinguished Name will be get by annotation @DistinguishedName if not
     * present or is null, will be get from value of @ParentDN annotation
     * concatenate with @Id
     * 
     * @param entry
     * @return Distinguished Name
     */
    public static String getDistinguishedName(Object entry) {
        isAnnotationPresent(entry.getClass(), LDAPEntry.class, true);

        Field idField = null;
        Field parentDnField = null;

        for (Field field : getSuperClassesFields(entry.getClass())) {
            if (field.isAnnotationPresent(DistinguishedName.class)) {
                Object dn = Reflections.getFieldValue(field, entry);
                if (dn != null)
                    return (String) dn;
            } else if (field.isAnnotationPresent(Id.class))
                idField = field;
            else if (field.isAnnotationPresent(ParentDN.class))
                parentDnField = field;
        }

        if (parentDnField != null && idField != null)
            return getFieldName(idField) + "=" + (String) Reflections.getFieldValue(idField, entry) + ","
                    + (String) Reflections.getFieldValue(parentDnField, entry);

        throw new EntryException(
                "Field with @DistinguishedName or fields with @ParentDN and @Id not found on class "
                        + entry.getClass().getSimpleName());
    }

    /**
     * Convert @LDAPEntry annotated object to Map(String, Object). The valid
     * types for Object Map value is String, String[] or byte[]
     * 
     * @param entry
     * @return Entry Map
     */
    public static Map<String, Object> getObjectMap(Object entry) {
        isAnnotationPresent(entry.getClass(), LDAPEntry.class, true);
        Map<String, Object> map = new HashMap<String, Object>();

        Field[] fields = getSuperClassesFields(entry.getClass());
        for (Field field : fields) {
            if (field.isAnnotationPresent(DistinguishedName.class) || field.isAnnotationPresent(Ignore.class)
                    || field.isAnnotationPresent(ParentDN.class))
                continue;
            // Ignore override attributes
            if (map.containsKey(getFieldName(field)))
                continue;
            Object value = Reflections.getFieldValue(field, entry);
            if (value == null)
                continue;
            if (field.isAnnotationPresent(RemoveOnMerge.class)) {
                map.put("@RemoveOnMerge", value);
                Reflections.setFieldValue(field, entry, null);
                continue;
            }
            map.put(getFieldName(field), getObjectAsSupportedType(value));
        }
        return map;
    }

    /**
     * Process field value of @LDAPEntry annotated class and return a LDAP
     * supported object of String, String[] or byte[] types
     * 
     * @return A value to be commited to LDAP as String, String[] or byte[]
     */
    public static Object getObjectAsSupportedType(Object obj) {
        if (obj instanceof String || obj instanceof String[] || obj instanceof byte[])
            return obj;
        else if (isAnnotationPresent(obj.getClass(), LDAPEntry.class, false))
            return getAnnotatedValue(obj, Id.class, true).toString();
        else if (obj.getClass().isArray())
            return convertToStringArrayFromArray(obj);
        else if (isCollection(obj.getClass()))
            return convertToStringArrayFromCollection(obj);
        else
            return obj.toString();
    }

    public static boolean isCollection(Class<?> clazz) {
        for (Class<?> claz : clazz.getInterfaces())
            if (claz == Collection.class)
                return true;
        return false;
    }

    public static String[] convertToStringArrayFromArray(Object array) {
        return new String[] { "Future implementation" };
    }

    public static String[] convertToStringArrayFromCollection(Object array) {
        return new String[] { "Future implementation" };
    }

    /**
     * Convert a Map(dn, Map(attr, Object)) to @LDAPEntry annotated object
     * 
     * @param entryMap
     * @param clazz
     *            <T>
     * @return List<T>
     */
    public static <T> List<T> getEntryObjectList(Map<String, Map<String, Object>> entryMap, Class<T> clazz) {
        return getEntryObjectList(entryMap, clazz, EntryObjectListCascade);
    }

    private static <T> List<T> getEntryObjectList(Map<String, Map<String, Object>> entryMap, Class<T> clazz,
            boolean cascade) {
        if (entryMap == null)
            return null;
        isAnnotationPresent(clazz, LDAPEntry.class, true);
        List<T> resultList = new ArrayList<T>();
        for (Map.Entry<String, Map<String, Object>> mapEntry : entryMap.entrySet())
            resultList.add(getEntryObject(mapEntry.getKey(), mapEntry.getValue(), clazz, cascade));
        return resultList;
    }

    public static <T> T getEntryObject(String dn, Map<String, Object> map, Class<T> clazz) {
        return getEntryObject(dn, map, clazz, EntryObjectCascade);
    }

    private static <T> T getEntryObject(String dn, Map<String, Object> map, Class<T> clazz, boolean cascade) {
        T entry = Beans.getReference(clazz);
        Field[] fields = getSuperClassesFields(entry.getClass());
        for (Field field : fields) {
            if (field.isAnnotationPresent(Ignore.class) || field.isAnnotationPresent(RemoveOnMerge.class))
                continue;

            if (field.isAnnotationPresent(ParentDN.class)) {
                setFieldValue(field, entry, new String[] { getParentDN(dn) });
                continue;
            }

            if (field.isAnnotationPresent(DistinguishedName.class)) {
                setFieldValue(field, entry, new String[] { dn });
                continue;
            }

            String fieldName = getFieldName(field);
            if (map.containsKey(fieldName))
                setFieldValue(field, entry, map.get(fieldName), cascade);
        }
        return entry;
    }

    public static String getParentDN(String dn) {
        return Strings.substringBeforeLast(dn, ",");
    }

    /**
     * Feeling a Class for a String Representation of Search Filters (RFC 4515)
     * and throw EntryException when can't find a @LDAPEntry object
     * 
     * @param searchFilter
     * @return Class
     */
    public static Class<?> getRequiredClassForSearchFilter(String searchFilter, List<String> packageNames) {
        Class<?> clazz = getClassForSearchFilter(searchFilter, packageNames);
        if (clazz == null)
            throw new EntryException("Can't find a @LDAPEntry object for search filter " + searchFilter
                    + ". Please, add all packages with @LDAPEntry classes to property list EntryManager.ldapentry.packages at demoiselle.properties file");
        return clazz;
    }

    /**
     * Feeling a Class or return null for a String Representation of Search
     * Filters (RFC 4515)
     * 
     * @param searchFilter
     * @return Class
     */
    public static Class<?> getClassForSearchFilter(String searchFilter, List<String> packageNames) {
        if (searchFilter == null || packageNames == null || packageNames.size() == 0)
            return null;
        String patternStr = "objectClass=(.*?)(\\)|$)";
        Matcher matcher = Pattern.compile(patternStr).matcher(searchFilter);
        boolean matchFound = matcher.find();
        if (matchFound && matcher.groupCount() > 1)
            for (String packageName : packageNames)
                try {
                    return Class.forName(packageName + "." + Character.toUpperCase(matcher.group(1).charAt(0))
                            + matcher.group(1).substring(1));
                } catch (Exception e) {
                }
        return null;
    }

    /**
     * Build a super classes List(Class(? extends Object))
     * 
     * @return List of Super Classes
     */
    public static List<Class<? extends Object>> getSuperClasses(Class<?> entryClass) {
        List<Class<? extends Object>> list = new ArrayList<Class<? extends Object>>();
        Class<? extends Object> superClazz = entryClass.getSuperclass();
        while (superClazz != null) {
            list.add(superClazz);
            superClazz = superClazz.getSuperclass();
        }
        return list;
    }

    /**
     * Build a array of super classes fields
     * 
     * @return Array of Super Classes Fields
     */
    public static Field[] getSuperClassesFields(Class<?> entryClass) {
        Field[] fieldArray = entryClass.getDeclaredFields();
        Class<?> superClazz = entryClass.getSuperclass();
        while (superClazz != null && !"java.lang.Object".equals(superClazz.getName())) {
            fieldArray = (Field[]) ArrayUtils.addAll(fieldArray, superClazz.getDeclaredFields());
            superClazz = superClazz.getSuperclass();
        }
        return fieldArray;
    }

    /**
     * Get annotated value
     * 
     * @param entry
     */
    public static Object getAnnotatedValue(Object entry, Class<? extends Annotation> aclazz, boolean required) {
        Field field = getFieldAnnotatedAs(entry.getClass(), aclazz, required);
        if (field != null)
            return Reflections.getFieldValue(field, entry);
        return null;
    }

    public static Field getFieldAnnotatedAs(Class<?> clazz, Class<? extends Annotation> aclazz, boolean required) {
        for (Field field : getSuperClassesFields(clazz))
            if (field.isAnnotationPresent(aclazz))
                return field;
        if (required)
            throw new EntryException(
                    "Field with @" + aclazz.getSimpleName() + " not found on class " + clazz.getSimpleName());
        else
            return null;
    }

    /**
     * Verify if a POJO have a annotated field, throw EntryException if
     * annotation is required and wasn't found.
     * 
     * @param clazz
     *            a POJO class to be verified
     * @param aclazz
     *            a Annotation class to be searched on POJO fields
     * @param required
     *            especify if should throw a EntryExpcetion when annotation
     *            was't found.
     * 
     */
    public static boolean hasFieldAnnotatedAs(Class<?> clazz, Class<? extends Annotation> aclazz,
            boolean required) {
        Field field = getFieldAnnotatedAs(clazz, aclazz, required);
        if (field == null)
            return false;
        return true;
    }

    /**
     * Value may be 'String', 'String[]' or Object Annotated with @LDAPEntry
     * 
     * @param field
     * @param entry
     * @param value
     */
    public static void setFieldValue(Field field, Object entry, Object value) {
        setFieldValue(field, entry, value, false);
    }

    public static void setFieldValue(Field field, Object entry, Object value, boolean cascade) {
        Reflections.setFieldValue(field, entry, getValueAsFieldType(field, value, cascade));
    }

    public static Object getValueAsFieldType(Field field, Object values, boolean cascade) {
        if (values instanceof String[]) {
            String[] valueArray = (String[]) values;

            if (valueArray == null || valueArray.length == 0)
                return null;

            if (field.getType().isAssignableFrom(String.class))
                return valueArray[0];

            if (field.getType().isArray())
                if (field.getType().getComponentType().isAssignableFrom(String.class))
                    return valueArray;

            if (field.getType().isPrimitive()) {

                if (field.getType().isAssignableFrom(int.class))
                    return Integer.valueOf(valueArray[0]);

                if (field.getType().isAssignableFrom(long.class))
                    return Long.valueOf(valueArray[0]);

                if (field.getType().isAssignableFrom(double.class))
                    return Double.valueOf(valueArray[0]);

                if (field.getType().isAssignableFrom(float.class))
                    return Float.valueOf(valueArray[0]);

                if (field.getType().isAssignableFrom(short.class))
                    return Short.valueOf(valueArray[0]);

                if (field.getType().isAssignableFrom(byte.class))
                    return Byte.valueOf(valueArray[0]);

            }

            if (isAnnotationPresent(field.getType(), LDAPEntry.class, false))
                return getMappedEntryObject(field.getType(), valueArray, cascade);

            if (field.getType().isAssignableFrom(ArrayList.class))
                if (String.class.isAssignableFrom(Reflections.getGenericTypeArgument(field.getType(), 0)))
                    return new ArrayList<String>(Arrays.asList(valueArray));

            if (field.getType().isAssignableFrom(Integer.class))
                return Integer.valueOf(valueArray[0]);

            if (field.getType().isAssignableFrom(Long.class))
                return Long.valueOf(valueArray[0]);

            if (field.getType().isAssignableFrom(Double.class))
                return Double.valueOf(valueArray[0]);

            if (field.getType().isAssignableFrom(Float.class))
                return Float.valueOf(valueArray[0]);

            if (field.getType().isAssignableFrom(Short.class))
                return Short.valueOf(valueArray[0]);

            if (field.getType().isAssignableFrom(Byte.class))
                return Byte.valueOf(valueArray[0]);

            logger.error("Handling not implemented for field " + field.getName() + " with type "
                    + field.getType().getSimpleName());

        } else if (values instanceof byte[][]) {

            if (field.getType().isAssignableFrom(byte[][].class))
                return values;

            if (field.getType().isAssignableFrom(byte[].class))
                return ((byte[][]) values)[0];

            logger.error("Binary data from LDAP can't be set in " + field.getName() + " with type "
                    + field.getType().getSimpleName());

        }

        logger.error("Object value should be String[] or byte[][]. The value type " + values.getClass()
                + " can't be set in " + field.getName() + " with type " + field.getType().getSimpleName());
        return null;
    }

    public static Object getMappedEntryObject(Class<?> clazz, String[] value, boolean cascade) {
        if (cascade) {
            EntryManager em = Beans.getReference(EntryManager.class);
            return em.find(clazz, value[0]);
        } else {
            Object entry = Beans.getReference(clazz);
            setAnnotatedFieldValueAs(entry, Id.class, value);
            return entry;
        }
    }

    /**
     * Set annotated value
     * 
     * @param entry
     */
    public static void setAnnotatedFieldValueAs(Object entry, Class<? extends Annotation> clazz, Object value) {
        Field field = getFieldAnnotatedAs(entry.getClass(), clazz, false);
        setFieldValue(field, entry, value);
    }

    /**
     * Get a field name (object attribute name) or the value of @Name if
     * annotation is present. In other words, if @Name is present returns
     * field.getAnnotation(Name.class).value(), otherwise field.getName();
     * 
     * @param field
     * @return @Name annotation value or object attribute name;
     */
    public static String getFieldName(Field field) {
        if (field.isAnnotationPresent(Name.class)) {
            String name = field.getAnnotation(Name.class).value();
            if (name == null || name.trim().isEmpty())
                throw new EntryException("Annotation @Name must have a value");
            return name;
        } else
            return field.getName();
    }

    /**
     * Verify if a class or yours super classes have annotation, throw
     * EntryException if annotation is required and wasn't found.
     * 
     * @param clazz
     *            a class to be verified
     * @param aclazz
     *            a Annotation class to be searched
     * @param required
     *            define if should throw a EntryExpcetion when annotation was't
     *            found.
     * 
     */
    public static boolean isAnnotationPresent(Class<?> clazz, Class<? extends Annotation> aclazz,
            boolean required) {
        for (Class<?> claz : getSuperClasses(clazz))
            if (claz.isAnnotationPresent(aclazz))
                return true;
        if (required)
            throw new EntryException("Entry " + clazz.getSimpleName() + " doesn't have @" + aclazz.getName());
        return false;
    }

}