com.intelligentsia.dowsers.entity.meta.provider.MetaEntityProviderAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for com.intelligentsia.dowsers.entity.meta.provider.MetaEntityProviderAnalyzer.java

Source

/**
 *        Licensed to the Apache Software Foundation (ASF) under one
 *        or more contributor license agreements.  See the NOTICE file
 *        distributed with this work for additional information
 *        regarding copyright ownership.  The ASF licenses this file
 *        to you under the Apache License, Version 2.0 (the
 *        "License"); you may not use this file except in compliance
 *        with the License.  You may obtain a copy of the License at
 *
 *          http://www.apache.org/licenses/LICENSE-2.0
 *
 *        Unless required by applicable law or agreed to in writing,
 *        software distributed under the License is distributed on an
 *        "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 *        KIND, either express or implied.  See the License for the
 *        specific language governing permissions and limitations
 *        under the License.
 *
 */
/**
 * 
 */
package com.intelligentsia.dowsers.entity.meta.provider;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.intelligentsia.dowsers.core.reflection.ClassInformation;
import org.intelligentsia.dowsers.core.reflection.Reflection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intelligentsia.dowsers.entity.Entity;
import com.intelligentsia.dowsers.entity.annotation.Attribute;
import com.intelligentsia.dowsers.entity.annotation.DomainEntity;
import com.intelligentsia.dowsers.entity.annotation.IgnoreAttribute;
import com.intelligentsia.dowsers.entity.meta.MetaAttribute;
import com.intelligentsia.dowsers.entity.meta.MetaEntity;
import com.intelligentsia.dowsers.entity.meta.MetaEntityProvider;
import com.intelligentsia.dowsers.entity.meta.MetaModel;
import com.intelligentsia.dowsers.entity.reference.Reference;

/**
 * <code>MetaEntityProviderAnalyzer</code> implements {@link MetaEntityProvider}
 * by analyzing specified {@link Entity} class in {@link Reference}.
 * 
 * @author <a href="mailto:jguibert@intelligents-ia.com">Jerome Guibert</a>
 * 
 */
public class MetaEntityProviderAnalyzer implements MetaEntityProvider {

    /**
     * {@link Logger} instance.
     */
    private final static Logger logger = LoggerFactory.getLogger(MetaEntityProviderAnalyzer.class);

    /**
     * Build a new instance of MetaEntityProviderAnalyzer.
     */
    public MetaEntityProviderAnalyzer() {
        super();
    }

    /**
     * @see com.intelligentsia.dowsers.entity.meta.MetaEntityProvider#find(com.intelligentsia.dowsers.entity.reference.Reference)
     */
    @Override
    public Collection<MetaEntity> find(final Reference reference) throws NullPointerException {
        Preconditions.checkNotNull(reference);
        final Collection<MetaEntity> result = Sets.newLinkedHashSet();
        if (reference.isIdentifier()) {
            return result;
        }
        // obtain class information
        final ClassInformation classInformation = ClassInformation.parse(reference.getEntityClassName());
        // analyze
        final MetaEntity metaEntity = analyze(classInformation);
        if (metaEntity != null) {
            if (!metaEntity.metaAttributes().isEmpty()) {
                result.add(metaEntity);
            }
        }
        return result;
    }

    /**
     * @param classInformation
     *            class Information to analyze
     * @return a {@link MetaEntity} instance
     * @throws NullPointerException
     *             if classInformation is null
     */
    @VisibleForTesting
    static MetaEntity analyze(final ClassInformation classInformation) throws NullPointerException {
        Preconditions.checkNotNull(classInformation);
        final Class<?> clazz = classInformation.getType();
        // init builder with class name and MetaModel version.
        final MetaEntity.Builder builder = MetaEntity.builder().name(clazz.getName()).version(MetaModel.VERSION);
        // analyze attributes
        final Iterator<MetaAttribute> iterator = analyzeMetaAttribute(clazz).iterator();
        while (iterator.hasNext()) {
            builder.metaAttribute(iterator.next());
        }
        return builder.build();
    }

    /**
     * Analyze MetaAttribute on specified class.
     * 
     * @param clazz
     * @return an instance of {@link List} of {@link MetaAttribute}
     * @throws NullPointerException
     *             if clazz is null
     * @throws IllegalArgumentException
     *             if something was wrong with attribute declaration
     */
    @VisibleForTesting
    static Collection<MetaAttribute> analyzeMetaAttribute(final Class<?> clazz)
            throws NullPointerException, IllegalArgumentException {
        final Map<String, MetaAttribute> attributes = Maps.newHashMap();
        Preconditions.checkNotNull(clazz);

        // auto discover attributes
        final DomainEntity domainEntity = clazz.getAnnotation(DomainEntity.class);
        final boolean autoDiscovering = domainEntity != null ? domainEntity.autoDiscovering() : Boolean.TRUE;

        // for each method
        for (final Method method : clazz.getMethods()) {
            if (hasAttributeSignature(method, autoDiscovering)) {
                final String name = extractName(method);
                if (!attributes.containsKey(name)) {
                    final Class<?> valueClass = extractValueClass(method);
                    logger.debug("Find attribute {}:{}", name, valueClass.getName());
                    attributes.put(name, MetaAttribute.builder().name(name).valueClass(valueClass).build());
                }
            }
        }

        // check for Identity attribute
        if (!attributes.containsKey(MetaModel.getIdentityAttribute().name())) {
            attributes.put(MetaModel.getIdentityAttribute().name(), MetaModel.getIdentityAttribute());
        } else {
            // check Identity attribute compliance
            if (!attributes.get(MetaModel.getIdentityAttribute().name()).equals(MetaModel.getIdentityAttribute())) {
                throw new IllegalArgumentException("Declared 'identity' attribute must be a Reference type");
            }
        }
        return attributes.values();
    }

    /**
     * Extract a method name.
     * 
     * @param method
     * @return a name
     */
    @VisibleForTesting
    static String extractName(final Method method) {
        final String name = method.getName();
        if (name.startsWith("get") || name.startsWith("set")) {
            return Reflection.toFieldName(name);
        }
        return Reflection.uncapitalize(name);
    }

    /**
     * @param method
     * @return class type
     */
    @VisibleForTesting
    static Class<?> extractValueClass(final Method method) {
        final Attribute attribute = method.getAnnotation(Attribute.class);
        if (attribute != null) {
            if (null != attribute.type()) {
                if (Void.TYPE != attribute.type()) {
                    return attribute.type();
                }
            }
        }
        if (Void.TYPE == method.getReturnType()) {
            return method.getParameterTypes()[0];
        }
        return method.getReturnType();
    }

    /**
     * Check if the specified method can be an attribute.
     * 
     * @param method
     * @return true if specified method can be an attribute, false other else.
     */
    @VisibleForTesting
    static boolean hasAttributeSignature(final Method method, final boolean autoDiscovering) {
        if (method.getAnnotation(IgnoreAttribute.class) != null) {
            return false;
        }
        if (!autoDiscovering && (method.getAnnotation(Attribute.class) == null)) {
            return false;
        }
        if (Void.TYPE == method.getReturnType()) {
            return method.getParameterTypes().length == 1;
        } else {
            return method.getParameterTypes().length == 0;
        }

    }
}