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

com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBDelimited 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 com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapperFieldModel.Id;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties.Bean;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardBeanProperties.Builder;
import com.amazonaws.services.dynamodbv2.datamodeling.StandardTypeConverters.Scalar;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import java.util.Map;
import java.util.regex.Pattern;

/**
 * Annotation to convert an object into a single delimited {@link String}
 * attribute.
 *
 * A minimal example using getter annotations,
 * 
 * @DynamoDBTable(tableName="TestTable")
 * public class TestClass {
 *     private String key;
 *     private PhoneNumber phoneNumber;
 *
 *     @DynamoDBHashKey
 *     public String getKey() { return key; }
 *     public void setKey(String key) { this.key = key; }
 *
 *     @DynamoDBDelimited(attributeNames={"areaCode","exchange","subscriber"}, delimiter='-')
 *     public PhoneNumber getPhoneNumber() { return phoneNumber; }
 *     public void setPhoneNumber(PhoneNumber phoneNumber) { this.phoneNumber = phoneNumber; }
 * }
 * 
* * With the following complex type to string delimit, *
 * public class PhoneNumber {
 *     private String areaCode;
 *     private String exchange;
 *     private String subscriber;
 *
 *     public String getAreaCode() { return areaCode; }
 *     public void setAreaCode(String areaCode) { this.areaCode = areaCode; }
 *
 *     public String getExchange() { return exchange; }
 *     public void setExchange(String exchange) { this.exchange = exchange; }
 *
 *     public String getSubscriber() { return subscriber; }
 *     public void setSubscriber(String subscriber) { this.subscriber = subscriber; }
 * }
 * 
* * Would write the following value to DynamoDB given, *
    *
  • PhoneNumber("206","266","1000") = "206-266-1000"
  • *
  • PhoneNumber("206",null,"1000") = "206--1000"
  • *
  • PhoneNumber("206",null,null) = "206--"
  • *
  • PhoneNumber(null,"266","1000") = "-266-1000"
  • *
  • PhoneNumber(null,"266",null) = "-266-"
  • *
  • PhoneNumber(null,null,"1000") = "--1000"
  • *
  • PhoneNumber(null,null,null) = null
  • *
  • null = null
  • *
* * Conversely, reading not fully formatted values from DynamoDB given, *
    *
  • "" = empty string not allowed by DDB but would produce empty object
  • *
  • "--" = PhoneNumber(null,null,null)
  • *
  • "-----" = PhoneNumber(null,null,null)
  • *
  • "206" = PhoneNumber("206",null,null)
  • *
  • "206-266" = PhoneNumber("206","266",null)
  • *
  • "206-266-1000-1234-5678" = PhoneNumber("206","266","1000")
  • *
* * Please note, * * The converter does not protect against values which may also contain the * delimiter. If more advanced conversion is required, consider implementing, * a custom {@link DynamoDBTypeConverter}. * * New delimited values may always be appended to the string, however, there * are some risks in distributed systems where, if one system has updated * delimiting instructions and begins to persist new values, other systems, * which also persist that same data, would effectively truncate it back to the * original format. * * Auto-generated annotations are not supported on field/property. However, * conversion annotations such as {@link DynamoDBTypeConvertedTimestamp} and * {@link DynamoDBTypeConverted} where the output type is {@link String} are * supported. * * @see com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConverted * @see com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTypeConvertedTimestamp */ @DynamoDBTypeConverted(converter=DynamoDBDelimited.Converter.class) @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) public @interface DynamoDBDelimited { /** * The delimiter for separating attribute values; default is |. */ char delimiter() default '|'; /** * The ordered list of attribute/field names. */ String[] attributeNames(); /** * Type converter for string delimited attributes. */ static final class Converter implements DynamoDBTypeConverter { private final Field[] fields; private final Class targetType; private final String delimiter; /** * Constructs a new delimited converter. * @param targetType The target type. * @param annotation The annotation. */ public Converter(final Class targetType, final DynamoDBDelimited annotation) { final String[] names = annotation.attributeNames(); if (names.length <= 1) { throw new DynamoDBMappingException("@DynamoDBDelimited missing attributeNames" + "; must specify two or more attribute names"); } final Map> beans = new Builder(targetType, true).build(); this.fields = new Field[names.length]; for (int i = 0; i < fields.length; i ++) { if (!beans.containsKey(names[i])) { throw new DynamoDBMappingException(new Id(targetType, names[i]) .format("not mapped on object model %s", beans.keySet())); } this.fields[i] = new Field(targetType, beans.get(names[i])); } this.delimiter = String.valueOf(annotation.delimiter()); this.targetType = targetType; } /** * {@inheritDoc} */ @Override public final String convert(final T object) { final StringBuilder string = new StringBuilder(); for (int i = 0; i < fields.length; i++) { if (i > 0) { string.append(delimiter); } final String value = fields[i].get(object); if (value != null) { if (value.contains(delimiter)) { throw new DynamoDBMappingException(fields[i].bean.id() .format("must not contain delimiter %s" + delimiter)); } string.append(value); } } return string.length() < fields.length ? null : string.toString(); } /** * {@inheritDoc} */ @Override public final T unconvert(final String string) { final T object; try { object = targetType.newInstance(); } catch (final Exception e) { throw new DynamoDBMappingException("unable to instantiate " + targetType, e); } final String[] values = string.split(Pattern.quote(delimiter)); for (int i = 0, its = Math.min(fields.length, values.length); i < its; i++) { fields[i].set(object, values[i]); } return object; } /** * Field conversion. */ private static final class Field { private final DynamoDBTypeConverter converter; private final Bean bean; private Field(final Class type, final Bean bean) { if (bean.annotations().typeConverted() == null) { this.converter = Scalar.STRING.getConverter(bean.type().type()); } else { this.converter = bean.annotations().typeConverter(); } this.bean = bean; } private final String get(final T object) { final V value = bean.reflect().get(object); if (value == null) { return null; } return converter.convert(value); } private final void set(final T object, final String string) { if (!string.isEmpty()) { final V value = converter.unconvert(string); if (value != null) { bean.reflect().set(object, value); } } } } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy