org.iternine.jeppetto.dao.dynamodb.expression.ProjectionExpressionBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.iternine.jeppetto.dao.dynamodb.expression.ProjectionExpressionBuilder.java

Source

/*
 * Copyright (c) 2011-2014 Jeppetto and Jonathan Thompson
 *
 * 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 org.iternine.jeppetto.dao.dynamodb.expression;

import com.amazonaws.services.dynamodbv2.model.Projection;
import com.amazonaws.services.dynamodbv2.model.ProjectionType;

import org.iternine.jeppetto.dao.annotation.Transient;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class ProjectionExpressionBuilder extends ExpressionBuilder {

    //-------------------------------------------------------------
    // Constants
    //-------------------------------------------------------------

    private static final Set<Class<?>> DIRECTLY_PROJECTED_TYPES = new HashSet<Class<?>>() {
        {
            add(Date.class);
            add(boolean.class);
            add(char.class);
            add(byte.class);
            add(short.class);
            add(int.class);
            add(long.class);
            add(float.class);
            add(double.class);
            add(Number.class);
            add(BigDecimal.class);
            add(BigInteger.class);
            add(String.class);
            add(Boolean.class);
            add(Character.class);
            add(Byte.class);
            add(Short.class);
            add(Integer.class);
            add(Long.class);
            add(Float.class);
            add(Double.class);
        }
    };

    private static final String EXPRESSION_ATTRIBUTE_NAME_PREFIX = "#p";

    //-------------------------------------------------------------
    // Variables - Private
    //-------------------------------------------------------------

    private final Set<String> nonKeyAttributes;
    private final String expression;

    //-------------------------------------------------------------
    // Constructors
    //-------------------------------------------------------------

    public ProjectionExpressionBuilder(Class<?> entityClass, String hashKeyField, String rangeKeyField,
            String optimisticLockField) {
        super(false);

        this.nonKeyAttributes = collectFields(entityClass, "", new HashSet<String>());

        nonKeyAttributes.remove(hashKeyField);
        nonKeyAttributes.remove(rangeKeyField);

        if (optimisticLockField != null) {
            // Add the optimistic lock field explicitly since it may not be exposed in the entity directly.
            nonKeyAttributes.add(optimisticLockField);
        }

        StringBuilder expressionStringBuilder = new StringBuilder(getExpressionAttributeName(hashKeyField));

        if (rangeKeyField != null) {
            expressionStringBuilder.append(", ").append(getExpressionAttributeName(rangeKeyField));
        }

        for (String nonKeyAttribute : nonKeyAttributes) {
            expressionStringBuilder.append(", ").append(nonKeyAttribute);
        }

        this.expression = expressionStringBuilder.toString();
    }

    //-------------------------------------------------------------
    // Implementation - ExpressionBuilder
    //-------------------------------------------------------------

    @Override
    public boolean hasExpression() {
        return expression.length() > 0;
    }

    @Override
    public String getExpression() {
        // NB: nonKeyAttributes no longer used once the expression is fetched.  Could make variable non-final and set to null here.

        return expression;
    }

    @Override
    public String getExpressionAttributeValuePrefix() {
        throw new RuntimeException("Should not be called");
    }

    @Override
    public String getExpressionAttributeNamePrefix() {
        return EXPRESSION_ATTRIBUTE_NAME_PREFIX;
    }

    //-------------------------------------------------------------
    // Methods - Public
    //-------------------------------------------------------------

    public Boolean isCoveredBy(Projection projection) {
        switch (ProjectionType.valueOf(projection.getProjectionType())) {
        case ALL:
            return Boolean.TRUE;
        case KEYS_ONLY:
            return nonKeyAttributes.isEmpty();
        case INCLUDE:
            return nonKeyAttributes.containsAll(projection.getNonKeyAttributes());
        }

        return Boolean.FALSE;
    }

    //-------------------------------------------------------------
    // Methods - Private
    //-------------------------------------------------------------

    private Set<String> collectFields(Class clazz, String fieldPrefix, Set<String> fields) {
        Map<String, Class> fieldsAndClasses = getFieldsAndClasses(clazz, fieldPrefix);

        for (Map.Entry<String, Class> entry : fieldsAndClasses.entrySet()) {
            String field = entry.getKey();
            Class fieldClass = entry.getValue();

            if (DIRECTLY_PROJECTED_TYPES.contains(fieldClass) || Collection.class.isAssignableFrom(fieldClass)) {
                fields.add(field);
            } else {
                collectFields(fieldClass, field + ".", fields);
            }
        }

        return fields;
    }

    private Map<String, Class> getFieldsAndClasses(Class clazz, String fieldPrefix) {
        Map<String, Class> fieldMap = new HashMap<String, Class>();

        List<Method> methods = new ArrayList<Method>();
        Collections.addAll(methods, clazz.getDeclaredMethods());
        Collections.addAll(methods, clazz.getMethods());

        for (Method method : methods) {
            if (method.getDeclaringClass().equals(Object.class) || method.getReturnType().equals(void.class)
                    || Modifier.isFinal(method.getModifiers()) || Modifier.isAbstract(method.getModifiers())
                    || method.getParameterTypes().length != 0 || method.getAnnotation(Transient.class) != null) {
                continue;
            }

            String methodName = method.getName();
            String upperCaseFieldName;

            if (methodName.startsWith("get")) {
                upperCaseFieldName = methodName.substring(3);
            } else if (methodName.startsWith("is")) {
                upperCaseFieldName = methodName.substring(2);
            } else {
                continue;
            }

            try {
                //noinspection unchecked
                clazz.getMethod("set".concat(upperCaseFieldName), method.getReturnType());
            } catch (NoSuchMethodException e) {
                continue;
            }

            String field = upperCaseFieldName.substring(0, 1).toLowerCase().concat(upperCaseFieldName.substring(1));

            fieldMap.put(fieldPrefix + getExpressionAttributeName(field), method.getReturnType());
        }

        return fieldMap;
    }
}