package com.impetus.kundera.validation.rules;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

import javassist.Modifier;

import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
import javax.persistence.Transient;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class EntityFieldAnnotationRule extends AbstractEntityRule implements EntityRule
{
    /** The Constant log. */
    private static final Logger log = LoggerFactory.getLogger(EntityFieldAnnotationRule.class);
    @Override
    public void validate(Class<?> clazz)
    {

      
        // Check for @Key and ensure that there is just 1 @Key field of
        // String
        // type.
        List<Field> keys = new ArrayList<Field>();
        for (Field field : clazz.getDeclaredFields())
        {
//            if (checkValidField(field))
//            {
                onIdField(field, clazz);
                if (field.isAnnotationPresent(Id.class))
                {
                    keys.add(field);
                    // validate @GeneratedValue annotation if given
                    if (field.isAnnotationPresent(GeneratedValue.class))
                    {
                        validateGeneratedValueAnnotation(clazz, field);
                    }
                }
                else if (field.isAnnotationPresent(EmbeddedId.class))
                {
                    keys.add(field);
                }
//            }
//            else
//            {
//
//                throw new RuleValidationException(" Invalid attribute annotation defined for : "
//                        + field.getName() + " of class " + clazz.getName());
//            }
        }

        // Check for a class key only in case it's of type entity or
        // MappedSuperClass
        if (clazz.isAnnotationPresent(Entity.class) || clazz.isAnnotationPresent(MappedSuperclass.class))
        {
            onSuperClass(clazz, keys);
        }

    }

    /**
     * Checks whether the defined class is a validone with an id field present
     * either in class itself or its superclass
     * 
     * @param clazz
     * @param keys
     * @throws RuleValidationException
     */
    private void onSuperClass(Class<?> clazz, List<Field> keys)
    {

        Class<?> superClass = clazz.getSuperclass();
        if (superClass != null
                && (superClass.isAnnotationPresent(MappedSuperclass.class) || superClass
                        .isAnnotationPresent(Entity.class)))
        {

            while (superClass != null
                    && (superClass.isAnnotationPresent(MappedSuperclass.class) || superClass
                            .isAnnotationPresent(Entity.class)))
            {
                keys = new ArrayList<Field>();
                for (Field field : superClass.getDeclaredFields())
                {

//                    if (checkValidField(field))
//                    {

                        onIdField(field, superClass);
                        if (field.isAnnotationPresent(Id.class))
                        {
                            keys.add(field);
                            // validate @GeneratedValue annotation if given
                            if (field.isAnnotationPresent(GeneratedValue.class))
                            {
                                validateGeneratedValueAnnotation(superClass, field);
                            }
                        }
                        else if (field.isAnnotationPresent(EmbeddedId.class))
                        {
                            keys.add(field);
                        }

                   // }
                }

                if (keys.size() > 0)
                {
                    onEntityKey(keys, superClass);
                    break;
                }
                superClass = superClass.getSuperclass();

            }
        }

        onEntityKey(keys, clazz);

    }

    /**
     * @param field
     * @param clazz
     */
    private void onIdField(Field field, Class<?> clazz)
    {

        if (field.isAnnotationPresent(Id.class) && field.isAnnotationPresent(EmbeddedId.class))
        {

            throw new RuleValidationException(clazz.getName()
                    + " must have either @Id field or @EmbeddedId field");
        }

    }

    /**
     * @param keys
     * @param clazz
     */
    private void onEntityKey(List<Field> keys, Class<?> clazz)
    {

        if (keys.size() <= 0)
        {

            throw new RuleValidationException(clazz.getName() + " must have an @Id field.");

        }
        else if (keys.size() > 1)
        {

            throw new RuleValidationException(clazz.getName() + " can only have 1 @Id field.");

        }

    }

    /**
     * @param field
     * @return
     */
    private boolean checkValidField(Field field)
    {
        if (field != null && !Modifier.isStatic(field.getModifiers()) && !Modifier.isTransient(field.getModifiers())
                && !field.isAnnotationPresent(Transient.class))
        {
            return field.isAnnotationPresent(Id.class) || field.isAnnotationPresent(EmbeddedId.class)
                    || field.isAnnotationPresent(Column.class) || field.isAnnotationPresent(ManyToMany.class)
                    || field.isAnnotationPresent(ManyToOne.class) || field.isAnnotationPresent(OneToOne.class)
                    || field.isAnnotationPresent(Embedded.class) || field.isAnnotationPresent(OneToMany.class)
                    || field.isAnnotationPresent(ElementCollection.class);
        }
        else
        {
            return true;
        }

    }

   
    /**
     * validate generated value annotation if given.
     * 
     * @param clazz
     * @param field
     * @throws RuleValidationException
     */
    private void validateGeneratedValueAnnotation(final Class<?> clazz, Field field)
    {

        Table table = clazz.getAnnotation(Table.class);
        // Still we need to validate for this after post metadata
        // population.
        if (table != null)
        {
            String schemaName = table.schema();
            if (schemaName != null && schemaName.indexOf('@') > 0)
            {
                schemaName = schemaName.substring(0, schemaName.indexOf('@'));
                GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);
                if (generatedValue != null && generatedValue.generator() != null
                        && !generatedValue.generator().isEmpty())
                {
                    if (!(field.isAnnotationPresent(TableGenerator.class)
                            || field.isAnnotationPresent(SequenceGenerator.class)
                            || clazz.isAnnotationPresent(TableGenerator.class) || clazz
                            .isAnnotationPresent(SequenceGenerator.class)))
                    {

                        throw new IllegalArgumentException("Unknown Id.generator: " + generatedValue.generator());
                    }
                    else
                    {
                        checkForGenerator(clazz, field, generatedValue, schemaName);
                    }
                }
            }
        }

    }

    /**
     * Validate for generator.
     * 
     * @param clazz
     * @param field
     * @param generatedValue
     * @param schemaName
     * @throws RuleValidationException
     */
    private void checkForGenerator(final Class<?> clazz, Field field, GeneratedValue generatedValue, String schemaName)

    {

        TableGenerator tableGenerator = field.getAnnotation(TableGenerator.class);
        SequenceGenerator sequenceGenerator = field.getAnnotation(SequenceGenerator.class);
        if (tableGenerator == null || !tableGenerator.name().equals(generatedValue.generator()))
        {
            tableGenerator = clazz.getAnnotation(TableGenerator.class);
        }
        if (sequenceGenerator == null || !sequenceGenerator.name().equals(generatedValue.generator()))
        {
            sequenceGenerator = clazz.getAnnotation(SequenceGenerator.class);
        }

        if ((tableGenerator == null && sequenceGenerator == null)
                || (tableGenerator != null && !tableGenerator.name().equals(generatedValue.generator()))
                || (sequenceGenerator != null && !sequenceGenerator.name().equals(generatedValue.generator())))
        {

            throw new RuleValidationException("Unknown Id.generator: " + generatedValue.generator());

        }
        else if ((tableGenerator != null && !tableGenerator.schema().isEmpty() && !tableGenerator.schema().equals(
                schemaName))
                || (sequenceGenerator != null && !sequenceGenerator.schema().isEmpty() && !sequenceGenerator.schema()
                        .equals(schemaName)))
        {

            throw new RuleValidationException("Generator " + generatedValue.generator() + " in entity : "
                    + clazz.getName() + " has different schema name ,it should be same as entity have");

        }

    }


}

@@ -3226,55 +3226,8 @@
   %7B%0A
-                keys = new ArrayList%3CField%3E();%0A
     

Fix for an issue where the @Id annotation rules were not properly validated.