Package info.archinnov.achilles.internal.metadata.parsing

Source Code of info.archinnov.achilles.internal.metadata.parsing.EmbeddedIdParser

/*
* Copyright (C) 2012-2014 DuyHai DOAN
*
*  Licensed 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 info.archinnov.achilles.internal.metadata.parsing;

import static info.archinnov.achilles.schemabuilder.Create.Options.ClusteringOrder;
import static info.archinnov.achilles.schemabuilder.Create.Options.ClusteringOrder.Sorting;
import static org.reflections.ReflectionUtils.getAllFields;

import java.lang.reflect.Field;
import java.util.*;

import info.archinnov.achilles.internal.metadata.holder.PropertyMeta;
import info.archinnov.achilles.internal.metadata.parsing.context.PropertyParsingContext;
import org.reflections.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Predicate;
import com.google.common.collect.FluentIterable;
import info.archinnov.achilles.annotations.Column;
import info.archinnov.achilles.annotations.Order;
import info.archinnov.achilles.annotations.PartitionKey;
import info.archinnov.achilles.exception.AchillesBeanMappingException;
import info.archinnov.achilles.internal.metadata.holder.EmbeddedIdProperties;
import info.archinnov.achilles.internal.metadata.holder.EmbeddedIdPropertiesBuilder;
import info.archinnov.achilles.internal.metadata.parsing.validator.PropertyParsingValidator;
import info.archinnov.achilles.internal.validation.Validator;

public class EmbeddedIdParser {

    private static final Logger log = LoggerFactory.getLogger(EmbeddedIdParser.class);
    private EntityIntrospector introspector = EntityIntrospector.Singleton.INSTANCE.get();
    private PropertyFilter filter = PropertyFilter.Singleton.INSTANCE.get();

    private final PropertyParsingContext context;

    public EmbeddedIdParser(PropertyParsingContext context) {
        this.context = context;
    }

    public EmbeddedIdProperties parseEmbeddedId(Class<?> embeddedIdClass, PropertyParser propertyParser) {
        log.debug("Parse embedded id class {} ", embeddedIdClass.getCanonicalName());

        Map<Integer, Field> components = extractComponentsOrdering(embeddedIdClass);
        validateConsistentPartitionKeys(components, embeddedIdClass.getCanonicalName());
        final List<ClusteringOrder> clusteringOrders = extractClusteringOrder(embeddedIdClass);
        EmbeddedIdProperties embeddedIdProperties = buildComponentMetas(propertyParser,embeddedIdClass, components, clusteringOrders);

        log.trace("Built embeddedId properties : {}", embeddedIdProperties);
        return embeddedIdProperties;
    }

    private Map<Integer, Field> extractComponentsOrdering(Class<?> embeddedIdClass) {
        log.trace("Extract components ordering from embedded id class {} ", embeddedIdClass.getCanonicalName());

        String embeddedIdClassName = embeddedIdClass.getCanonicalName();
        Map<Integer, Field> components = new TreeMap<>();

        @SuppressWarnings("unchecked")
        Set<Field> candidateFields = getAllFields(embeddedIdClass, ReflectionUtils.<Field>withAnnotation(Order.class));

        Set<Integer> orders = new HashSet<>();
        int orderSum = 0;
        int componentCount = candidateFields.size();

        for (Field candidateField : candidateFields) {
            Order orderAnnotation = candidateField.getAnnotation(Order.class);
            int order = orderAnnotation.value();
            Class<?> componentType = candidateField.getType();
            orderSum = validateNoDuplicateOrderAndType(embeddedIdClassName, orders, orderSum, order, componentType);
            components.put(order, candidateField);
        }

        validateConsistentOrdering(embeddedIdClassName, orderSum, componentCount);
        Validator.validateBeanMappingTrue(componentCount > 1,
                "There should be at least 2 fields annotated with @Order for the @EmbeddedId class '%s'",
                embeddedIdClass.getCanonicalName());
        return components;

    }

    private List<ClusteringOrder> extractClusteringOrder(Class<?> embeddedIdClass) {
        log.debug("Extract clustering component order from embedded id class {} ",embeddedIdClass.getCanonicalName());

        List<ClusteringOrder> sortOrders = new LinkedList<>();

        @SuppressWarnings("unchecked")
        Set<Field> candidateFields = getAllFields(embeddedIdClass, ReflectionUtils.withAnnotation(Order.class));
        final List<Field> clusteringFields = FluentIterable.from(candidateFields).filter(new Predicate<Field>() {
            @Override
            public boolean apply(Field field) {
                Order orderAnnotation = field.getAnnotation(Order.class);
                return !filter.hasAnnotation(field, PartitionKey.class) && orderAnnotation.value() > 1;
            }
        }).toSortedList(new Comparator<Field>() {
            @Override
            public int compare(Field o1, Field o2) {
                Order order1 = o1.getAnnotation(Order.class);
                Order order2 = o2.getAnnotation(Order.class);
                return new Integer(order1.value()).compareTo(new Integer(order2.value()));
            }
        });

        for (Field clusteringField : clusteringFields) {
            final Order order = clusteringField.getAnnotation(Order.class);
            final String cqlColumnName = introspector.inferCQLColumnName(clusteringField, context.getClassNamingStrategy());
            validateNotStaticColumn(clusteringField);
            sortOrders.add(new ClusteringOrder(cqlColumnName, order.reversed() ? Sorting.DESC : Sorting.ASC));
        }

        return sortOrders;

    }

    private int validateNoDuplicateOrderAndType(String embeddedIdClassName, Set<Integer> orders, int orderSum,
            int order, Class<?> componentType) {
        log.debug("Validate type and component ordering for embedded id class {} ", embeddedIdClassName);
        Validator.validateBeanMappingTrue(orders.add(order), "The order '%s' is duplicated in @EmbeddedId class '%s'",
                order, embeddedIdClassName);

        orderSum += order;

        PropertyParsingValidator.validateAllowedTypes(componentType, PropertyParser.allowedTypes, "The class '"
                + componentType.getCanonicalName() + "' is not a valid component type for the @EmbeddedId class '"
                + embeddedIdClassName + "'");
        return orderSum;
    }

    private void validateConsistentOrdering(String embeddedIdClassName, int orderSum, int componentCount) {
        int check = (componentCount * (componentCount + 1)) / 2;

        log.debug("Validate component ordering for @EmbeddedId class {} ", embeddedIdClassName);

        Validator.validateBeanMappingTrue(orderSum == check, "The component ordering is wrong for @EmbeddedId class '%s'", embeddedIdClassName);
    }

    private void validateConsistentPartitionKeys(Map<Integer, Field> componentsOrdering, String embeddedIdClassName) {
        log.debug("Validate composite partition key component ordering for @EmbeddedId class {} ", embeddedIdClassName);
        int orderSum = 0;
        int orderCount = 0;
        for (Integer order : componentsOrdering.keySet()) {
            Field componentField = componentsOrdering.get(order);
            if (filter.hasAnnotation(componentField, PartitionKey.class)) {
                orderSum = orderSum + order;
                orderCount++;
            }
        }

        int check = (orderCount * (orderCount + 1)) / 2;
        Validator.validateBeanMappingTrue(orderSum == check, "The composite partition key ordering is wrong for @EmbeddedId class '%s'", embeddedIdClassName);
    }

    private EmbeddedIdProperties buildComponentMetas(PropertyParser propertyParser, Class<?> embeddedIdClass, Map<Integer, Field> components, List<ClusteringOrder> clusteringOrders) {

        log.debug("Build components meta data for embedded id class {}", embeddedIdClass.getCanonicalName());
        EmbeddedIdPropertiesBuilder partitionKeysBuilder = new EmbeddedIdPropertiesBuilder();
        EmbeddedIdPropertiesBuilder clusteringKeysBuilder = new EmbeddedIdPropertiesBuilder();
        clusteringKeysBuilder.setClusteringOrders(clusteringOrders);

        buildPartitionAndClusteringKeys(propertyParser, embeddedIdClass, components,partitionKeysBuilder, clusteringKeysBuilder);

        return EmbeddedIdPropertiesBuilder.buildEmbeddedIdProperties(partitionKeysBuilder.buildPartitionKeys(), clusteringKeysBuilder.buildClusteringKeys(), context.getCurrentEntityClass().getCanonicalName());
    }

    private void buildPartitionAndClusteringKeys(PropertyParser propertyParser, Class<?> embeddedIdClass, Map<Integer, Field> components,
            EmbeddedIdPropertiesBuilder partitionKeysBuilder,EmbeddedIdPropertiesBuilder clusteringKeysBuilder) {
        log.debug("Build Components meta data for embedded id class {}", embeddedIdClass.getCanonicalName());

        for (Integer order : components.keySet()) {
            Field compositeKeyField = components.get(order);

            final PropertyMeta propertyMeta = propertyParser.parseSimpleProperty(context.duplicateForField(compositeKeyField));

            if (filter.hasAnnotation(compositeKeyField, PartitionKey.class)) {
                partitionKeysBuilder.addPropertyMeta(propertyMeta);
            } else {
                clusteringKeysBuilder.addPropertyMeta(propertyMeta);
            }
        }

        // If not @PartitionKey annotation found, take the first field as default partition key
        if (partitionKeysBuilder.getPropertyMetas().isEmpty()) {
            final PropertyMeta partitionMeta = clusteringKeysBuilder.getPropertyMetas().remove(0);
            partitionKeysBuilder.addPropertyMeta(partitionMeta);
        }
    }

    private void validateNotStaticColumn(Field compositeKeyField) {
        Column column = compositeKeyField.getAnnotation(Column.class);
        if (column != null && column.staticColumn()) {
            throw new AchillesBeanMappingException(String.format("The property '%s' of class '%s' cannot be a static column because it belongs to the primary key",compositeKeyField.getName(),compositeKeyField.getDeclaringClass().getCanonicalName()));
        }
    }
}
TOP

Related Classes of info.archinnov.achilles.internal.metadata.parsing.EmbeddedIdParser

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.