package de.ppi.fuwesta.spring.mvc.util;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.persistence.Entity;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.SystemPropertyUtils;

/**
 * Add all Entity-Information to messages, the key has the form
 * db.<simpleclassname>.<propertyname>.
 * 
 */
public class EntityPropertiesToMessages {

    /**
     * The Logger for the controller.
     */
    private static final Logger LOG = LoggerFactory
            .getLogger(EntityPropertiesToMessages.class);

    /**
     * the messages.
     */
    private final Properties messages = new Properties();

    private final String[] packageNames;

    private boolean analyzed = false;

    public EntityPropertiesToMessages(String... packageName) {
        this.packageNames = packageName;
    }

    public void analyze() {
        final List<Class<?>> entities = new ArrayList<>();
        for (String packageName : packageNames) {

            try {
                entities.addAll(findEntities(packageName));
            } catch (IOException e) {
                LOG.error("Error finding entities-classes", e);
            }
        }
        for (Class<?> entity : entities) {
            List<Field> fields = getAllFields(entity);
            for (Field field : fields) {
                final String key =
                        "db." + entity.getSimpleName() + "." + field.getName();
                messages.put(key, field.getName());
            }

        }
        analyzed = true;
    }

    public Properties getProperties() {
        if (!analyzed) {
            analyze();
        }
        return messages;
    }

    private List<Class<?>> findEntities(String basePackage) throws IOException {
        ResourcePatternResolver resourcePatternResolver =
                new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metadataReaderFactory =
                new CachingMetadataReaderFactory(resourcePatternResolver);

        final List<Class<?>> candidates = new ArrayList<Class<?>>();
        final String packageSearchPath =
                ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
                        + resolveBasePackage(basePackage) + "/" + "**/*.class";
        final Resource[] resources =
                resourcePatternResolver.getResources(packageSearchPath);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                MetadataReader metadataReader =
                        metadataReaderFactory.getMetadataReader(resource);
                try {
                    if (isCandidate(metadataReader)) {
                        candidates.add(Class.forName(metadataReader
                                .getClassMetadata().getClassName()));
                    }
                } catch (ClassNotFoundException cnfE) {
                    LOG.error("Error finding entity-classes", cnfE);
                }
            }
        }
        return candidates;
    }

    private String resolveBasePackage(String basePackage) {
        return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils
                .resolvePlaceholders(basePackage));
    }

    private boolean isCandidate(MetadataReader metadataReader)
            throws ClassNotFoundException {
        Class<?> c =
                Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(Entity.class) != null) {
            return true;
        }
        return false;
    }

    private List<Field> getAllFields(Class<?> type) {
        final List<Field> fields = new ArrayList<>();
        for (Field field : type.getDeclaredFields()) {
            if (!Modifier.isStatic(field.getModifiers())) {
                fields.add(field);
            }
        }

        if (type.getSuperclass() != null) {
            fields.addAll(getAllFields(type.getSuperclass()));
        }
        return fields;
    }
}

@@ -885,17 +885,20 @@
 m%0A * db.
-%3C
+&lt;
 simplecl
@@ -908,11 +908,17 @@
 name
-%3E.%3C
+&gt;.&lt;
 prop
@@ -925,17 +925,20 @@
 ertyname
-%3E
+&gt;
 .%0A * %0A *

Fix a JavaDoc error.