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

com.amazonaws.services.dynamodbv2.document.Item 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.12.772
Show newest version
/*
 * Copyright 2014-2018 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.
 * A copy of the License is located at
 *
 *  http://aws.amazon.com/apache2.0
 *
 * or in the "license" file accompanying this file. 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.document;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.checkInvalidAttrName;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.checkInvalidAttribute;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.rejectNullInput;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.rejectNullOrEmptyInput;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.rejectNullValue;
import static com.amazonaws.services.dynamodbv2.document.internal.InternalUtils.valToString;
import static com.amazonaws.util.BinaryUtils.copyAllBytesFrom;
import static com.amazonaws.util.BinaryUtils.copyBytesFrom;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils;
import com.amazonaws.services.dynamodbv2.document.internal.ItemValueConformer;
import com.amazonaws.util.Base64;
import com.amazonaws.util.json.Jackson;
/**
 * An item in DynamoDB. An item is a collection of attributes. Each attribute
 * has a name and a value. An attribute value can be one of the followings:
 * 
    *
  • String
  • *
  • Set<String>
  • *
  • Number (including any subtypes and primitive types)
  • *
  • Set<Number>
  • *
  • byte[]
  • *
  • Set<byte[]>
  • *
  • ByteBuffer
  • *
  • Set<ByteBuffer>
  • *
  • Boolean or boolean
  • *
  • null
  • *
  • Map<String,T>, where T can be any type on this list but must not * induce any circular reference
  • *
  • List<T>, where T can be any type on this list but must not induce * any circular reference
  • *
* For an Item to be successfully persisted in DynamoDB, at a * minimum the respective attributes for the primary key must be specified. */ public class Item { private static final String DUPLICATE_VALUES_FOUND_IN_INPUT = "Duplicate values found in input"; private final Map attributes = new LinkedHashMap(); private static final ItemValueConformer valueConformer = new ItemValueConformer(); /** * Returns true if the specified attribute exists with a null value; false * otherwise. */ public boolean isNull(String attrName) { return attributes.containsKey(attrName) && attributes.get(attrName) == null; } /** * Returns true if this item contains the specified attribute; false * otherwise. */ public boolean isPresent(String attrName) { return attributes.containsKey(attrName); } /** * Returns the value of the specified attribute in the current item as a * string; or null if the attribute either doesn't exist or the attribute * value is null. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public String getString(String attrName) { Object val = attributes.get(attrName); return valToString(val); } /** * Sets the value of the specified attribute in the current item to the * given string value. */ public Item withString(String attrName, String val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Returns the value of the specified attribute in the current item as a * BigDecimal; or null if the attribute either doesn't exist or * the attribute value is null. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException if the attribute value is not a valid * representation of a {@code BigDecimal}. */ public BigDecimal getNumber(String attrName) { Object val = attributes.get(attrName); return toBigDecimal(val); } private BigDecimal toBigDecimal(Object val) { if (val == null) return null; return val instanceof BigDecimal ? (BigDecimal) val : new BigDecimal(val.toString()) ; } /** * Returns the value of the specified attribute in the current item as an * BigInteger; or null if the attribute doesn't exist. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public BigInteger getBigInteger(String attrName) { BigDecimal bd = getNumber(attrName); return bd == null ? null : bd.toBigInteger(); } /** * Returns the value of the specified attribute in the current item as a * short. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public short getShort(String attrName) { BigDecimal bd = getNumber(attrName); if (bd == null) throw new NumberFormatException ("value of " + attrName + " is null"); return bd.shortValue(); } /** * Returns the value of the specified attribute in the current item as an * int. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public int getInt(String attrName) { BigDecimal bd = getNumber(attrName); if (bd == null) throw new NumberFormatException ("value of " + attrName + " is null"); return bd.intValue(); } /** * Returns the value of the specified attribute in the current item as an * long. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public long getLong(String attrName) { BigDecimal bd = getNumber(attrName); if (bd == null) throw new NumberFormatException ("value of " + attrName + " is null"); return bd.longValue(); } /** * Returns the value of the specified attribute in the current item as a * float. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public float getFloat(String attrName) { BigDecimal bd = getNumber(attrName); if (bd == null) throw new NumberFormatException ("value of " + attrName + " is null"); return bd.floatValue(); } /** * Returns the value of the specified attribute in the current item as a * double. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws NumberFormatException * if the attribute value is null or not a valid representation * of a {@code BigDecimal}. */ public double getDouble(String attrName) { BigDecimal bd = getNumber(attrName); if (bd == null) throw new NumberFormatException ("value of " + attrName + " is null"); return bd.doubleValue(); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withNumber(String attrName, BigDecimal val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withNumber(String attrName, Number val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, toBigDecimal(val)); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withInt(String attrName, int val) { checkInvalidAttrName(attrName); return withNumber(attrName, Integer.valueOf(val)); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBigInteger(String attrName, BigInteger val) { checkInvalidAttrName(attrName); return withNumber(attrName, val); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withShort(String attrName, short val) { checkInvalidAttrName(attrName); return withNumber(attrName, Short.valueOf(val)); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withFloat(String attrName, float val) { checkInvalidAttrName(attrName); return withNumber(attrName, Float.valueOf(val)); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withDouble(String attrName, double val) { checkInvalidAttrName(attrName); return withNumber(attrName, Double.valueOf(val)); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withLong(String attrName, long val) { checkInvalidAttrName(attrName); return withNumber(attrName, Long.valueOf(val)); } /** * Returns the value of the specified attribute in the current item as a * byte array; or null if the attribute either doesn't exist or the * attribute value is null. * * @throws UnsupportedOperationException * If the attribute value involves a byte buffer which is not * backed by an accessible array * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a byte array * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public byte[] getBinary(String attrName) { Object val = attributes.get(attrName); return toByteArray(val); } /** * Returns the value of the specified attribute in the current item as a * ByteBuffer; or null if the attribute either doesn't exist or * the attribute value is null. * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a byte array * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public ByteBuffer getByteBuffer(String attrName) { Object val = attributes.get(attrName); return toByteBuffer(val); } /** * This method is assumed to be only called from a getter method, but NOT * from a setter method. */ private byte[] toByteArray(Object val) { if (val == null) return null; if (val instanceof byte[]) return (byte[]) val; if (val instanceof ByteBuffer) { // Defensive code but execution should never get here. The internal // representation of binary should always be // byte[], not ByteBuffer. This allows Item to be converted into // a JSON string via Jackson without causing trouble. return copyAllBytesFrom((ByteBuffer)val); } throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a byte array"); } private ByteBuffer toByteBuffer(Object val) { if (val == null) return null; if (val instanceof byte[]) return ByteBuffer.wrap((byte[])val); if (val instanceof ByteBuffer) { // Defensive code but execution should never get here. The internal // representation of binary should always be // byte[], not ByteBuffer. This allows Item to be converted into // a JSON string via Jackson without causing trouble. return (ByteBuffer)val; } throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a ByteBuffer"); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBinary(String attrName, byte[] val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBinary(String attrName, ByteBuffer val) { checkInvalidAttribute(attrName, val); // convert ByteBuffer to bytes to keep Jackson happy attributes.put(attrName, copyBytesFrom(val)); return this; } /** * Returns the value of the specified attribute in the current item as a set * of strings; or null if the attribute either doesn't exist or the * attribute value is null. * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a set of * strings because of duplicate elements * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Set getStringSet(String attrName) { Object val = attributes.get(attrName); if (val == null) return null; Set stringSet = new LinkedHashSet(); if (val instanceof Collection) { Collection col = (Collection) val; if (col.size() == 0) return stringSet; for (Object element: col) { String s = element == null ? null : valToString(element); if (!stringSet.add(s)) throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of strings because of duplicate elements"); } return stringSet; } stringSet.add(valToString(val)); return stringSet; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withStringSet(String attrName, Set val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withStringSet(String attrName, String ...val) { checkInvalidAttribute(attrName, val); Set strSet = new LinkedHashSet(Arrays.asList(val)); if (strSet.size() != val.length) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); attributes.put(attrName, strSet); return this; } /** * Returns the value of the specified attribute in the current item as a set * of BigDecimal's; or null if the attribute either doesn't exist or the * attribute value is null. * * @throws NumberFormatException * if the attribute involves a value that is not a valid * representation of a {@code BigDecimal}. * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a set of * BigDecimal's because of duplicate elements * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Set getNumberSet(String attrName) { Object val = attributes.get(attrName); if (val == null) return null; Set numSet = new LinkedHashSet(); if (val instanceof Collection) { Collection col = (Collection) val; if (col.size() == 0) return numSet; for (Object element: col) { BigDecimal bd = toBigDecimal(element); if (!numSet.add(bd)) throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of BigDecimal's because of duplicate elements"); } return numSet; } else if (val instanceof BigDecimal) { numSet.add((BigDecimal)val); return numSet; } else { numSet.add(new BigDecimal(val.toString())); return numSet; } } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBigDecimalSet(String attrName, Set val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBigDecimalSet(String attrName, BigDecimal ... vals) { checkInvalidAttribute(attrName, vals); Set set = new LinkedHashSet(Arrays.asList(vals)); if (set.size() != vals.length) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); attributes.put(attrName, set); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withNumberSet(String attrName, Number ... vals) { checkInvalidAttribute(attrName, vals); Set set = InternalUtils.toBigDecimalSet(vals); if (set.size() != vals.length) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); return withBigDecimalSet(attrName, set); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withNumberSet(String attrName, Set vals) { checkInvalidAttribute(attrName, vals); Set set = InternalUtils.toBigDecimalSet(vals); if (set.size() != vals.size()) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); return withBigDecimalSet(attrName, set); } /** * Returns the value of the specified attribute in the current item as a set * of byte arrays; or null if the attribute either doesn't exist or the * attribute value is null. * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a set of byte * arrays * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Set getBinarySet(String attrName) { Object val = attributes.get(attrName); if (val == null) return null; Set binarySet = new LinkedHashSet(); if (val instanceof Collection) { Collection col = (Collection) val; if (col.size() == 0) return binarySet; for (Object element: col) { byte[] ba = toByteArray(element); if (!binarySet.add(ba)) throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of byte arrays because of duplicate elements"); } return binarySet; } else if (val instanceof byte[]) { binarySet.add((byte[])val); return binarySet; } else if (val instanceof ByteBuffer) { // Defensive code but execution should never get here. The internal // representation of binary should always be // byte[], not ByteBuffer. This allows Item to be converted into // a JSON string via Jackson without causing trouble. ByteBuffer bb = (ByteBuffer) val; binarySet.add(copyAllBytesFrom(bb)); return binarySet; } throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of byte arrays"); } /** * Returns the value of the specified attribute in the current item as a set * of ByteBuffer; or null if the attribute either doesn't exist * or the attribute value is null. * * @throws IncompatibleTypeException * if the attribute value cannot be converted into a set of * ByteBuffer * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Set getByteBufferSet(String attrName) { Object val = attributes.get(attrName); if (val == null) return null; Set binarySet = new LinkedHashSet(); if (val instanceof Collection) { Collection col = (Collection) val; if (col.size() == 0) return binarySet; for (Object element: col) { ByteBuffer ba = toByteBuffer(element); if (!binarySet.add(ba)) throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of ByteBuffer because of duplicate elements"); } return binarySet; } else if (val instanceof ByteBuffer) { // Defensive code but execution should never get here. The internal // representation of binary should always be // byte[], not ByteBuffer. This allows Item to be converted into // a JSON string via Jackson without causing trouble. binarySet.add((ByteBuffer)val); return binarySet; } else if (val instanceof byte[]) { binarySet.add(ByteBuffer.wrap((byte[])val)); return binarySet; } throw new IncompatibleTypeException(val.getClass() + " cannot be converted into a set of ByteBuffer"); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBinarySet(String attrName, Set val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, val); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withByteBufferSet(String attrName, Set val) { checkInvalidAttribute(attrName, val); // convert ByteBuffer to bytes to keep Jackson happy Set set = new LinkedHashSet(val.size()); for (ByteBuffer bb: val) set.add(copyBytesFrom(bb)); attributes.put(attrName, set); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBinarySet(String attrName, byte[] ... vals) { checkInvalidAttribute(attrName, vals); Set set = new LinkedHashSet(Arrays.asList(vals)); if (set.size() != vals.length) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); attributes.put(attrName, set); return this; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withBinarySet(String attrName, ByteBuffer ... vals) { checkInvalidAttribute(attrName, vals); // convert ByteBuffer to bytes to keep Jackson happy Set set = new LinkedHashSet(vals.length); for (ByteBuffer bb: vals) set.add(copyBytesFrom(bb)); if (set.size() != vals.length) throw new IllegalArgumentException(DUPLICATE_VALUES_FOUND_IN_INPUT); attributes.put(attrName, set); return this; } /** * Returns the value of the specified attribute in the current item as a set * of T's.; or null if the attribute either doesn't exist or * the attribute value is null. * * @throws ClassCastException * if the attribute involves a value that cannot be casted to * T * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public List getList(String attrName) { Object val = attributes.get(attrName); if (val == null) return null; if (val instanceof List) { @SuppressWarnings("unchecked") List ret = (List)val; return ret; } List list = new ArrayList(); if (val instanceof Collection) { Collection col = (Collection)val; for (Object element: col) { @SuppressWarnings("unchecked") T t = (T)element; list.add(t); } return list; } @SuppressWarnings("unchecked") T t = (T)val; list.add(t); return list; } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withList(String attrName, List val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, valueConformer.transform(val)); return this; } /** * Sets the value of the specified attribute in the current item to the * given values as a list. */ public Item withList(String attrName, Object ... vals) { checkInvalidAttribute(attrName, vals); List list_in = Arrays.asList(vals); attributes.put(attrName, valueConformer.transform(list_in)); return this; } /** * Returns the value of the specified attribute in the current item as a map * of string-to-T's; or null if the attribute either doesn't * exist or the attribute value is null. Note that any numeric type of a * map is always canonicalized into BigDecimal, and therefore * if T referred to a Number type, it would need * to be BigDecimal to avoid a class cast exception. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws ClassCastException * if the attribute is not a map of string to T */ @SuppressWarnings("unchecked") public Map getMap(String attrName) { return (Map)attributes.get(attrName); } /** * Convenient method to return the specified attribute in the current item * as a (copy of) map of string-to-T's where T must be a * subclass of Number; or null if the attribute doesn't * exist. * * @param attrName * the attribute name * @param valueType * the specific number type of the value to be returned. * Currently, only
    *
  • Short
  • *
  • Integer
  • *
  • Long
  • *
  • Float
  • *
  • Double
  • *
  • Number
  • *
  • BigDecimal
  • *
  • BigInteger
  • *
are supported. * * @throws UnsupportedOperationException * if the value type is not supported * @throws ClassCastException * if the attribute is not a map of string to numbers */ @SuppressWarnings("unchecked") public Map getMapOfNumbers(String attrName, Class valueType) { if (valueType == Short.class || valueType == Integer.class || valueType == Long.class || valueType == Float.class || valueType == Double.class || valueType == Number.class || valueType == BigDecimal.class || valueType == BigInteger.class) { final Map src = (Map)attributes.get(attrName); if (src == null) return null; final Map dst = new LinkedHashMap(src.size()); for (Map.Entry e: src.entrySet()) { final String key = e.getKey(); final BigDecimal val = e.getValue(); if (val == null) { dst.put(key, null); } else if (valueType == Short.class) { dst.put(key, (T)Short.valueOf(val.shortValue())); } else if (valueType == Integer.class) { dst.put(key, (T)Integer.valueOf(val.intValue())); } else if (valueType == Long.class) { dst.put(key, (T)Long.valueOf(val.longValue())); } else if (valueType == Float.class) { dst.put(key, (T)Float.valueOf(val.floatValue())); } else if (valueType == Double.class) { dst.put(key, (T)Double.valueOf(val.doubleValue())); } else if (valueType == BigDecimal.class || valueType == Number.class) { dst.put(key, (T)val); } else if (valueType == BigInteger.class) { dst.put(key, (T)val.toBigInteger()); } } return dst; } else { throw new UnsupportedOperationException("Value type " + valueType + " is not currently supported"); } } /** * Convenient method to return the value of the specified attribute in the * current item as a map of string-to-Object's; or null if the * attribute either doesn't exist or the attribute value is null. Note that * any numeric type of the map will be returned as BigDecimal. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. * * @throws ClassCastException if the attribute is not a map */ @SuppressWarnings("unchecked") public Map getRawMap(String attrName) { return (Map)attributes.get(attrName); } /** * Sets the value of the specified attribute in the current item to the * given value. */ public Item withMap(String attrName, Map val) { checkInvalidAttribute(attrName, val); attributes.put(attrName, valueConformer.transform(val)); return this; } /** * Sets the value of the specified attribute in the current item to the * given JSON document in the form of a string. */ public Item withJSON(String attrName, String json) { checkInvalidAttribute(attrName, json); attributes.put(attrName, valueConformer.transform(Jackson.fromJsonString(json, Object.class))); return this; } /** * Returns the value of the specified attribute in the current item as a * JSON string; or null if the attribute either doesn't * exist or the attribute value is null. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public String getJSON(String attrName) { checkInvalidAttrName(attrName); Object val = attributes.get(attrName); return val == null ? null : Jackson.toJsonString(val); } /** * Returns the value of the specified attribute in the current item as a * JSON string with pretty indentation; or null if the attribute either * doesn't exist or the attribute value is null. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public String getJSONPretty(String attrName) { checkInvalidAttrName(attrName); Object val = attributes.get(attrName); return val == null ? null : Jackson.toJsonPrettyString(val); } /** * Returns the value of the specified attribute in the current item as a * non-null Boolean. * * @throws IncompatibleTypeException * if either the attribute doesn't exist or if the attribute * value cannot be converted into a non-null Boolean value * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Boolean getBOOL(String attrName) { final Object val = attributes.get(attrName); if (val instanceof Boolean) return (Boolean)val; if (val instanceof String) { if ("1".equals(val)) return true; if ("0".equals(val)) return false; return Boolean.valueOf((String)val); } throw new IncompatibleTypeException("Value of attribute " + attrName + " of type " + getTypeOf(attrName) + " cannot be converted into a boolean value"); } /** * Returns the value of the specified attribute in the current item as a * primitive boolean. * * @throws IncompatibleTypeException * if either the attribute doesn't exist or if the attribute * value cannot be converted into a boolean value */ public boolean getBoolean(String attrName) { final Boolean b = getBOOL(attrName); return b.booleanValue(); } /** * Sets the value of the specified attribute in the current item to the * boolean value. */ public Item withBoolean(String attrName, boolean val) { checkInvalidAttrName(attrName); attributes.put(attrName, Boolean.valueOf(val)); return this; } /** * Sets the value of the specified attribute to null. */ public Item withNull(String attrName) { checkInvalidAttrName(attrName); attributes.put(attrName, null); return this; } /** * Sets the value of the specified attribute to the given value. An * attribute value can be a *
    *
  • Number
  • *
  • String
  • *
  • binary (ie byte array or byte buffer)
  • *
  • boolean
  • *
  • null
  • *
  • list (of any of the types on this list)
  • *
  • map (with string key to value of any of the types on this list)
  • *
  • set (of any of the types on this list)
  • *
*/ public Item with(String attrName, Object val) { if (val == null) return withNull(attrName); if (val instanceof String) return withString(attrName, (String)val); if (val instanceof Number) return withNumber(attrName, (Number)val); if (val instanceof byte[]) return withBinary(attrName, (byte[])val); if (val instanceof ByteBuffer) return withBinary(attrName, (ByteBuffer)val); if (val instanceof Boolean) return withBoolean(attrName, (Boolean)val); if (val instanceof List) return withList(attrName, (List)val); if (val instanceof Map) { @SuppressWarnings("unchecked") Map map = (Map)val; return withMap(attrName, map); } if (val instanceof Set) { Set set = (Set)val; // Treat an empty set as a set of String if (set.size() == 0) { @SuppressWarnings("unchecked") Set ss = (Set)val; return withStringSet(attrName, ss); } // Try to locate the first non-null element and use that as the // representative type Object representative = null; for (Object o: set) { if (o != null) representative = o; } // If all elements are null, treat the element type as String if (representative == null || representative instanceof String) { @SuppressWarnings("unchecked") Set ss = (Set)val; return withStringSet(attrName, ss); } if (representative instanceof Number) { @SuppressWarnings("unchecked") Set ns = (Set)val; return withNumberSet(attrName, ns); } if (representative instanceof byte[]) { @SuppressWarnings("unchecked") Set bs = (Set)val; return withBinarySet(attrName, bs); } if (representative instanceof ByteBuffer) { @SuppressWarnings("unchecked") Set bs = (Set)val; return withByteBufferSet(attrName, bs); } throw new UnsupportedOperationException("Set of " + representative.getClass() + " is not currently supported"); } throw new UnsupportedOperationException("Input type " + val.getClass() + " is not currently supported"); } /** * Convenient methods - sets the attributes of this item from the given * key attributes. */ public Item withPrimaryKey(PrimaryKey primaryKey) { rejectNullValue(primaryKey); if (primaryKey.getComponents().size() == 0) throw new IllegalArgumentException("primary key must not be empty"); for (KeyAttribute ka: primaryKey.getComponents()) this.with(ka.getName(), ka.getValue()); return this; } /** * Convenient method to set the attributes of this item from the given * hash-only primary key name and value. */ public Item withPrimaryKey(String hashKeyName, Object hashKeyValue) { return withKeyComponent(hashKeyName, hashKeyValue); } /** * Convenient method to set the attributes of this item from the given * hash and range primary key. */ public Item withPrimaryKey(String hashKeyName, Object hashKeyValue, String rangeKeyName, Object rangeKeyValue) { return withKeyComponent(hashKeyName, hashKeyValue) .withKeyComponent(rangeKeyName, rangeKeyValue); } /** * Convenient methods - sets the attributes of this item from the specified * key components. */ public Item withKeyComponents(KeyAttribute ...components) { rejectNullOrEmptyInput(components); for (KeyAttribute ka: components) { rejectNullValue(ka); this.with(ka.getName(), ka.getValue()); } return this; } /** * Convenient methods - sets an attribute of this item for the specified * key attribute name and value. */ public Item withKeyComponent(String keyAttrName, Object keyAttrValue) { return with(keyAttrName, keyAttrValue); } /** * Returns the value of the specified attribute in the current item as an * object; or null if the attribute either doesn't exist or the attribute * value is null. *

* An attribute value can be a *

    *
  • Number
  • *
  • String
  • *
  • binary (ie byte array or byte buffer)
  • *
  • boolean
  • *
  • null
  • *
  • list (of any of the types on this list)
  • *
  • map (with string key to value of any of the types on this list)
  • *
  • set (of any of the types on this list)
  • *
* * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Object get(String attrName) { return attributes.get(attrName); } /** * Returns the type of the specified attribute in the current item; or null * if the attribute either doesn't exist or the attribute value is null. * * @see #isNull(String) #isNull(String) to check if the attribute value is * null. * @see #isPresent(String) #isPresent(String) to check if the attribute * value is present. */ public Class getTypeOf(String attrName) { Object val = attributes.get(attrName); return val == null ? null : val.getClass(); } /** * Removes the specified attribute from the current item. */ public Item removeAttribute(String attrName) { checkInvalidAttrName(attrName); attributes.remove(attrName); return this; } /** * Returns all attributes of the current item. */ public Iterable> attributes() { return new LinkedHashMap(attributes).entrySet(); } /** * Returns true if this item has the specified attribute; false otherwise. */ public boolean hasAttribute(String attrName) { return attributes.containsKey(attrName); } /** * Returns all attributes of the current item as a map. */ public Map asMap() { return new LinkedHashMap(attributes); } /** * Returns the number of attributes of this item. */ public int numberOfAttributes() { return attributes.size(); } /** * Convenient factory method - instantiates an Item from the * given map. * * @param attributes * simple Java types; not the DyanmoDB types */ public static Item fromMap(Map attributes) { if (attributes == null) return null; Item item = new Item(); for (Map.Entry e : attributes.entrySet()) item.with(e.getKey(), e.getValue()); return item; } /** * Convenient factory method - instantiates an Item from the * given JSON string. * * @return an Item initialized from the given JSON document; * or null if the input is null. */ public static Item fromJSON(String json) { if (json == null) return null; @SuppressWarnings("unchecked") Map map = (Map) valueConformer.transform(Jackson.fromJsonString(json, Map.class)); return fromMap(map); } /** * Returns this item as a JSON string. Note all binary data will become * base-64 encoded in the resultant string. */ public String toJSON() { return Jackson.toJsonString(this.attributes); } /** * Utility method to decode the designated binary attributes from base-64 * encoding; converting binary lists into binary sets. * * @param binaryAttrNames * names of binary attributes or binary set attributes currently * base-64 encoded (typically when converted from a JSON string.) * * @see #fromJSON(String) */ public Item base64Decode(String ... binaryAttrNames) { rejectNullInput(binaryAttrNames); // Verify all attributes are good for (String attrName: binaryAttrNames) { checkInvalidAttrName(attrName); if (String.class == getTypeOf(attrName)) { String b64 = getString(attrName); Base64.decode(b64); } else { Set b64s = getStringSet(attrName); for (String b64: b64s) Base64.decode(b64); } } // Decodes b64 into binary for (String attrName: binaryAttrNames) { if (String.class == getTypeOf(attrName)) { String b64 = getString(attrName); byte[] bytes = Base64.decode(b64); withBinary(attrName, bytes); } else { Set b64s = getStringSet(attrName); Set binarySet = new LinkedHashSet(b64s.size()); for (String b64: b64s) binarySet.add(Base64.decode(b64)); withBinarySet(attrName, binarySet); } } return this; } /** * Utility method to converts the designated attributes from * List into Set, throwing * IllegalArgumentException should there be duplicate elements. * * @param listAttrNames * names of attributes to be converted. * * @see #fromJSON(String) */ public Item convertListsToSets(String ... listAttrNames) { rejectNullInput(listAttrNames); // Verify all attributes are good for (String attrName: listAttrNames) { checkInvalidAttrName(attrName); if (List.class.isAssignableFrom(getTypeOf(attrName))) { List list = getList(attrName); if (list != null) { for (Object e: list) { if (e instanceof String) { Set ss = getStringSet(attrName); if (list.size() != ss.size()) throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); } else if (e instanceof Number) { Set ss = getNumberSet(attrName); if (list.size() != ss.size()) throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); } else if (e instanceof byte[]) { Set ss = getBinarySet(attrName); if (list.size() != ss.size()) throw new IllegalArgumentException("List cannot be converted to Set due to duplicate elements"); } } } } else { throw new IllegalArgumentException("Attribute " + attrName + " is not a list"); } } // Do the conversion for (String attrName: listAttrNames) { checkInvalidAttrName(attrName); List list = getList(attrName); if (list != null) { boolean converted = false; for (Object e: list) { if (e instanceof String) { Set set = getStringSet(attrName); withStringSet(attrName, set); converted = true; break; } else if (e instanceof Number) { Set set = getNumberSet(attrName); withBigDecimalSet(attrName, set); converted = true; break; } else if (e instanceof byte[]) { Set set = getBinarySet(attrName); withBinarySet(attrName, set); converted = true; break; } } if (!converted) { // All elements are null. So treat it as a String set. Set set = getStringSet(attrName); withStringSet(attrName, set); } } } return this; } /** * Returns this item as a pretty JSON string. Note all binary data will * become base-64 encoded in the resultant string. */ public String toJSONPretty() { return Jackson.toJsonPrettyString(this.attributes); } @Override public String toString() { return "{ Item: " + attributes.toString() + " }"; } @Override public int hashCode() { return attributes.hashCode(); } @Override public boolean equals(Object in) { if (in instanceof Item) { Item that = (Item)in; return this.attributes.equals(that.attributes); } else { return false; } } }