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 SDK for Java with support for OSGi. The AWS SDK for Java provides Java APIs for building software on AWS' cost-effective, scalable, and reliable infrastructure products. The AWS Java SDK allows developers to code against APIs for all of Amazon's infrastructure web services (Amazon S3, Amazon EC2, Amazon SQS, Amazon Relational Database Service, Amazon AutoScaling, etc).

There is a newer version: 1.11.60
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