All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.amazonaws.services.dynamodbv2.datamodeling.StandardConverterRules Maven / Gradle / Ivy

Go to download

The AWS Java SDK for Amazon DynamoDB module holds the client classes that are used for communicating with Amazon DynamoDB Service

There is a newer version: 1.9.11
Show newest version
/*
 * Copyright 2016-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
 *
 * 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://aws.amazon.com/apache2.0
 *
 * This file 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.amazonaws.services.dynamodbv2.datamodeling;

import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.B;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.BOOL;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.BS;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.L;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.M;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.N;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.NULL;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.NS;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.S;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType.SS;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar.BOOLEAN;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar.BYTE_BUFFER;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar.DEFAULT;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar.STRING;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Vector.LIST;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Vector.MAP;
import static com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Vector.SET;

import com.amazonaws.annotation.SdkInternalApi;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.DynamoDBAttributeType;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Properties;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Reflect;
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverterFactory.OverrideFactory;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardAttributeTypes.AttributeType;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties.Bean;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardParameterTypes.ParamType;
import com.amazonaws.services.dynamodbv2.model.AttributeValue;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * Pre-defined strategies for mapping between Java types and DynamoDB types.
 */
@SdkInternalApi
final class StandardConverterRules {

    private static final Log LOG = LogFactory.getLog(StandardConverterRules.class);

    /**
     * Creates a new set of conversion rules based on the configuration.
     */
    static final  RuleFactory of(final DynamoDBMapperConfig config, final DynamoDBMapperModelFactory.Factory factory, final S3ClientCache s3cc) {
        final Rules rules = new Rules(StandardTypeConverters.factory());
        rules.with(String.class, S3Link.class, new S3Link.Factory(s3cc));
        if (config.getConversionSchema() == ConversionSchemas.V1) {
            rules.with(rules.new NativeBool(true));
            rules.with(rules.new SimpleScalar(S));
            rules.with(rules.new SimpleScalar(N));
            rules.with(rules.new SimpleScalar(B));
            rules.with(rules.new SimpleScalarSet(SS));
            rules.with(rules.new SimpleScalarSet(NS));
            rules.with(rules.new SimpleScalarSet(BS));
            rules.with(rules.new ObjectToStringSet());
        } else if (config.getConversionSchema() == ConversionSchemas.V2) {
            rules.with(rules.new NativeBool(false));
            rules.with(rules.new SimpleScalar(S));
            rules.with(rules.new SimpleScalar(N));
            rules.with(rules.new SimpleScalar(B));
            rules.with(rules.new NativeBoolSet());
            rules.with(rules.new SimpleScalarSet(SS));
            rules.with(rules.new SimpleScalarSet(NS));
            rules.with(rules.new SimpleScalarSet(BS));
            rules.with(rules.new ObjectSetOrList());
            rules.with(rules.new ObjectStringKeyMap());
            rules.with(rules.new ObjectDocumentMap(config, factory));
        } else {
            rules.with(rules.new NativeBool(true));
            rules.with(rules.new SimpleScalar(S));
            rules.with(rules.new SimpleScalar(N));
            rules.with(rules.new SimpleScalar(B));
            rules.with(rules.new SimpleScalarSet(SS));
            rules.with(rules.new SimpleScalarSet(NS));
            rules.with(rules.new SimpleScalarSet(BS));
            rules.with(rules.new ObjectToStringSet());
            rules.with(rules.new ObjectSetOrList());
            rules.with(rules.new ObjectStringKeyMap());
            rules.with(rules.new ObjectDocumentMap(config, factory));
        }
        return rules;
    }

    /**
     * Groups the conversion rules to be evaluated.
     */
    private static final class Rules extends OverrideFactory implements RuleFactory {
        private final Set> rules = new LinkedHashSet>();

        /**
         * Constructs a new conversion evaluator with the specified scalar
         * type-converter factory.
         */
        private Rules(final DynamoDBTypeConverterFactory converters) {
            super(converters);
        }

        /**
         * Adds a conversion rule.
         */
        private Rules with(final Rule rule) {
            this.rules.add((Rule)rule);
            return this;
        }

        /**
         * Returns the first conversion rule matching the specified type;
         * scalar attribute type may be specified to override the standard
         * source conversion, otherwise, it should be left as null.
         */
        private Rule getRule(final ParamType type, final Properties props) {
            if (props.typeConverter() != null) {
                return new CustomTypeConverted(props);
            }
            for (final Rule rule : rules) {
                if (rule.isAssignableFrom(type, props)) {
                    return rule;
                }
            }
            return new NotSupported();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Rule getRule(final Bean bean) {
            return getRule(bean.type(), bean);
        }

        /**
         * Custom type-converted conversion.
         */
        private final class CustomTypeConverted implements Rule {
            private final DynamoDBTypeConverter target;
            private final ParamType sourceType;
            private final Rule rule;
            private CustomTypeConverted(final Properties props) {
                this.target = props.typeConverter();
                this.sourceType = StandardParameterTypes.of(target);
                this.rule = getRule(sourceType, new Properties.Buildable(props).withTypeConverter(null));
            }
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                return true;
            }
            @Override
            public DynamoDBTypeConverter newConverter(final ParamType type) {
                final DynamoDBTypeConverter source = rule.newConverter(sourceType);
                return join(source, target);
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return rule.getAttributeType();
            }
        }

        /**
         * Native boolean conversion.
         */
        private final class NativeBool implements Rule {
            private final boolean onlyIfOverride;
            private NativeBool(final boolean onlyIfOverride) {
                this.onlyIfOverride = onlyIfOverride;
            }
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return !onlyIfOverride && SET.is(type.type()) == false && BOOLEAN.is(type.type());
                }
                return props.attributeType() == getAttributeType();
            }
            @Override
            public DynamoDBTypeConverter newConverter(final ParamType type) {
                return BOOL.join(BOOLEAN.join(type.type()));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return BOOL.attributeType();
            }
        }

        /**
         * Scalar conversions.
         */
        private final class SimpleScalar implements Rule {
            private final AttributeType attribute;
            private final Class sourceType;
            private SimpleScalar(final AttributeType attribute) {
                this.sourceType = (attribute == B ? BYTE_BUFFER : STRING).type();
                this.attribute = attribute;
            }
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return SET.is(type.type()) == false && type.scalar().is(attribute.scalarAttributeType());
                }
                return props.attributeType() == getAttributeType();
            }
            @Override
            public DynamoDBTypeConverter newConverter(final ParamType type) {
                final DynamoDBTypeConverter target = getConverter(sourceType, type.type());
                return attribute.join(target);
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return attribute.attributeType();
            }
        }

        /**
         * Scalar set conversions.
         */
        private final class SimpleScalarSet implements Rule> {
            private final AttributeType attribute;
            private final Class sourceType;
            private SimpleScalarSet(final AttributeType attribute) {
                this.sourceType = (attribute == BS ? BYTE_BUFFER : STRING).type();
                this.attribute = attribute;
            }
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return SET.is(type.type()) && type.param(0).scalar().is(attribute.scalarAttributeType());
                }
                return props.attributeType() == getAttributeType();
            }
            @Override
            public DynamoDBTypeConverter> newConverter(final ParamType> type) {
                final DynamoDBTypeConverter target = getConverter(sourceType, type.param(0).type());
                return attribute.join(SET.join(target));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return attribute.attributeType();
            }
        }

        /**
         * V1 Object set conversions.
         */
        private final class ObjectToStringSet implements Rule> {
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return SET.is(type.type());
                }
                return false;
            }
            @Override
            public DynamoDBTypeConverter> newConverter(final ParamType> type) {
                LOG.warn("Marshaling a set of non-String objects to a DynamoDB StringSet. " +
                    "You won't be able to read these objects back out of DynamoDB unless " +
                    "you REALLY know what you're doing: it's probably a bug. If you DO know " +
                    "what you're doing feel to ignore this warning, but consider using a " +
                    "custom @DynamoDBTypeConverted/DynamoDBTypeConverter for this instead.");
                return SS.join(SET.join(STRING.join(DEFAULT.type())));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return SS.attributeType();
            }
        }

        /**
         * Native bool {@link Set} conversions.
         */
        private final class NativeBoolSet implements Rule> {
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return SET.is(type.type()) && BOOLEAN.is(type.param(0).type());
                }
                return false;
            }
            @Override
            public DynamoDBTypeConverter> newConverter(final ParamType> type) {
                return join(new DynamoDBTypeConverter>() {
                    public final AttributeValue convert(final List o) {
                        return L.convert(o);
                    }
                    public final List unconvert(final AttributeValue o) {
                        if (o.getL() == null && o.getNS() != null) {
                            return LIST.convert(o.getNS(), BOOL.join(BOOLEAN.join(String.class)));
                        }
                        return (List)L.unconvert(o);
                    }
                }, SET.join(NULL.join(BOOL.join(BOOLEAN.join(type.param(0).type())))));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return L.attributeType();
            }
        }

        /**
         * Any {@link List} conversions.
         */
        private final class ObjectSetOrList implements Rule> {
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return (SET.is(type.type()) || LIST.is(type.type())) && type.param(0) != null;
                }
                return false;
            }
            @Override
            public DynamoDBTypeConverter> newConverter(final ParamType> type) {
                final Rule rule = getRule(type.param(0), Properties.Immutable.empty());
                final DynamoDBTypeConverter target = NULL.join(rule.newConverter(type.param(0)));
                return L.join(SET.is(type.type()) ? SET.join(target) : LIST.join(target));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return L.attributeType();
            }
        }

        /**
         * Any {@link Map} conversions.
         */
        private final class ObjectStringKeyMap implements Rule> {
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return MAP.is(type.type()) && type.param(1) != null && STRING.is(type.param(0).type());
                }
                return false;
            }
            @Override
            public DynamoDBTypeConverter> newConverter(final ParamType> type) {
                final Rule rule = getRule(type.param(1), Properties.Immutable.empty());
                final DynamoDBTypeConverter target = NULL.join(rule.newConverter(type.param(1)));
                return M.join(MAP.join(target));
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return M.attributeType();
            }
        }

        /**
         * All {@link DynamoDBDocument} conversions.
         */
        private final class ObjectDocumentMap implements Rule {
            private final DynamoDBMapperModelFactory.Factory factory;
            private final DynamoDBMapperConfig config;
            private ObjectDocumentMap(final DynamoDBMapperConfig config, final DynamoDBMapperModelFactory.Factory factory) {
                this.factory = factory;
                this.config = config;
            }
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                if (props.attributeType() == null) {
                    return StandardBeanProperties.of(type.type()).annotations().document() != null;
                }
                return props.attributeType() == getAttributeType();
            }
            @Override
            public DynamoDBTypeConverter newConverter(final ParamType type) {
                return M.join(new DynamoDBTypeConverter,V>() {
                    public final Map convert(final V o) {
                        return factory.getModelFactory(config).getTableModel(type.type()).convert(o);
                    }
                    public final V unconvert(final Map o) {
                        return factory.getModelFactory(config).getTableModel(type.type()).unconvert(o);
                    }
                });
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return M.attributeType();
            }
        }

        /**
         * Default conversion when no match could be determined.
         */
        private final class NotSupported implements Rule {
            @Override
            public boolean isAssignableFrom(final ParamType type, final Properties props) {
                return false;
            }
            @Override
            public DynamoDBTypeConverter newConverter(final ParamType type) {
                return new DynamoDBTypeConverter() {
                    public final AttributeValue convert(final V o) {
                        throw new DynamoDBMappingException("type " + type + " is not supported" +
                            "; requires @DynamoDBDocument or @DynamoDBTypeConverted");
                    }
                    public final V unconvert(final AttributeValue o) {
                        throw new DynamoDBMappingException("type " + type + " is not supported" +
                            "; requires @DynamoDBDocument or @DynamoDBTypeConverted");
                    }
                };
            }
            @Override
            public DynamoDBAttributeType getAttributeType() {
                return NULL.attributeType();
            }
        }
    }

    /**
     * Attribute value conversion.
     */
    static interface Rule {
        boolean isAssignableFrom(ParamType type, Properties props);
        DynamoDBTypeConverter newConverter(ParamType type);
        DynamoDBAttributeType getAttributeType();
    }

    /**
     * Attribute value conversion factory.
     */
    static interface RuleFactory {
        Rule getRule(Bean bean);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy