/* * Copyright 2011-2012 Amazon Technologies, Inc. * * 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.eclipse.dynamodb.editor; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Map; import com.amazonaws.services.dynamodbv2.document.internal.InternalUtils; import com.amazonaws.services.dynamodbv2.model.AttributeValue; import com.amazonaws.services.dynamodbv2.model.ScalarAttributeType; import com.amazonaws.util.BinaryUtils; import com.amazonaws.util.json.Jackson; /** * Utility methods for working with attribute values */ public class AttributeValueUtil { /** * Data type constants corresponding to the six fields of the * {@link AttributeValue} object. */ static final int S = 0; static final int SS = 1; static final int N = 2; static final int NS = 3; static final int B = 4; static final int BS = 5; /** * Sets exactly one field of the {@link AttributeValue} given, clearing all * others. The field set is determined by the datatype. */ static void setAttribute(AttributeValue attributeValue, final Collection<String> newValue, int dataType) { clearAttributes(attributeValue); if ( newValue.isEmpty() ) return; switch (dataType) { case NS: attributeValue.setNS(newValue); break; case SS: attributeValue.setSS(newValue); break; case BS: attributeValue.setBS(getByteBuffers(newValue)); break; case N: attributeValue.setN(newValue.iterator().next()); break; case S: attributeValue.setS(newValue.iterator().next()); break; case B: attributeValue.setB(getByteBuffer(newValue.iterator().next())); break; default: throw new RuntimeException("Unknown data type " + dataType); } } /** * Gets a byte buffer corresponding to the base64 string given. */ private static ByteBuffer getByteBuffer(String base64) { byte[] binary = BinaryUtils.fromBase64(base64); return ByteBuffer.wrap(binary); } /** * Returns a list of ByteBuffers corresponding to the base64 strings given. */ private static Collection<ByteBuffer> getByteBuffers(Collection<String> newValue) { List<ByteBuffer> buffers = new LinkedList<ByteBuffer>(); for (String value : newValue) { buffers.add(getByteBuffer(value)); } return buffers; } static void setAttribute(AttributeValue attributeValue, final Collection<String> newValue, String dataType) { if ( ScalarAttributeType.N.toString().equals(dataType) ) { setAttribute(attributeValue, newValue, N); } else if ( ScalarAttributeType.S.toString().equals(dataType) ) { setAttribute(attributeValue, newValue, S); } else if ( ScalarAttributeType.B.toString().equals(dataType) ) { setAttribute(attributeValue, newValue, B); } else { throw new RuntimeException("Unknown data type " + dataType); } } /** * Translates the data types returned by some Dynamo apis into the integers * used by this class. */ static int getDataType(String dataType) { if ( ScalarAttributeType.S.toString().equals(dataType) ) { return S; } else if ( ScalarAttributeType.N.toString().equals(dataType) ) { return N; } else if ( ScalarAttributeType.B.toString().equals(dataType) ) { return B; } else if ( "SS".equals(dataType) ) { return SS; } else if ( "NS".equals(dataType) ) { return NS; } else if ( "BS".equals(dataType) ) { return BS; } else { throw new RuntimeException("Unknown data type " + dataType); } } /** * Clears all fields from the object given. */ static void clearAttributes(AttributeValue attributeValue) { attributeValue.setSS(null); attributeValue.setNS(null); attributeValue.setS(null); attributeValue.setN(null); attributeValue.setB(null); attributeValue.setBS(null); } /** * Formats the value of the given {@link AttributeValue} for display, * joining list elements with a comma and enclosing them in brackets. */ static String format(final AttributeValue value) { if ( value == null ) return ""; if ( value.getN() != null ) return value.getN(); else if ( value.getNS() != null ) return join(value.getNS()); else if ( value.getS() != null ) return value.getS(); else if ( value.getSS() != null ) return join(value.getSS(), true); else if ( value.getB() != null ) return base64Format(value.getB()); else if ( value.getBS() != null ) return joinBase64(value.getBS()); else if ( value.getBOOL() != null ) return value.getBOOL() ? "true" : "false"; else if ( value.getNULL() != null ) return value.getNULL() ? "null" : ""; else if ( value.getM() != null ) return formatMapAttribute(value.getM()); else if ( value.getL() != null ) return formatListAttribute(value.getL()); return ""; } private static String formatMapAttribute(Map<String, AttributeValue> map) { if (map == null) return ""; try { Map<String, Object> objectMap = InternalUtils.toSimpleMapValue(map); return Jackson.toJsonString(objectMap); } catch (Exception e) { return "A map of " + map.size() + " entries"; } } private static String formatListAttribute(List<AttributeValue> list) { if (list == null) return ""; try { List<Object> objectList = InternalUtils.toSimpleListValue(list); return Jackson.toJsonString(objectList); } catch (Exception e) { return "A list of " + list.size() + " entries"; } } /** * Returns the given byte buffer list as a base-64 formatted list */ private static String joinBase64(List<ByteBuffer> bs) { Collection<String> base64Strings = base64FormatOfBinarySet(bs); return join(base64Strings); } /** * Returns a base-64 string of the given bytes */ private static String base64Format(ByteBuffer b) { return BinaryUtils.toBase64(b.array()); } /** * Returns a base-64 string of the given bytes */ private static Collection<String> base64FormatOfBinarySet(Collection<ByteBuffer> bs) { List<String> base64Strings = new LinkedList<String>(); for (ByteBuffer b : bs) { base64Strings.add(base64Format(b)); } return base64Strings; } /** * Joins a collection of values with commas, enclosed by brackets. An empty * or null set of values returns the empty string. */ static String join(final Collection<String> values) { return join(values, false); } /** * Joins a collection of values with commas, enclosed by brackets. An empty * or null set of values returns the empty string. * * @param quoted * Whether each value should be quoted in the output. */ static String join(final Collection<String> values, boolean quoted) { if ( values == null || values.isEmpty() ) { return ""; } StringBuilder builder = new StringBuilder("{"); boolean seenOne = false; for ( String s : values ) { if ( seenOne ) { builder.append(","); } else { seenOne = true; } builder.append(quoted ? "\"" + s + "\"" : s); } builder.append("}"); return builder.toString(); } /** * Returns the values from this {@link AttributeValue} as a collection, * which may contain only one element or be empty. */ static Collection<String> getValuesFromAttribute(AttributeValue value) { if ( value == null ) return Collections.emptyList(); if ( value.getN() != null ) { return Arrays.asList(value.getN()); } else if ( value.getNS() != null ) { return value.getNS(); } else if ( value.getS() != null ) { return Arrays.asList(value.getS()); } else if ( value.getSS() != null ) { return value.getSS(); } else if ( value.getB() != null ) { return Arrays.asList(base64Format(value.getB())); } else if ( value.getBS() != null ) { return base64FormatOfBinarySet(value.getBS()); } else { return Collections.emptyList(); } } /** * Validates the user input of a scalar attribute value. */ public static boolean validateScalarAttributeInput(String attributeInput, int dataType, boolean acceptEmpty) { if ( null == attributeInput ) { return false; } if ( attributeInput.isEmpty() ) { return acceptEmpty; } if ( dataType == S ) { return attributeInput.length() > 0; } else if ( dataType == N ) { return validateNumberStringInput(attributeInput); } else if ( dataType == B ) { return validateBase64StringInput(attributeInput); } else { return false; } } /** * Returns the warning message for the given attribute type. */ public static String getScalarAttributeValidationWarning(String attributeName, int dataType) { if ( dataType == S ) { return "Invalid String value for " + attributeName + "."; } else if ( dataType == N ) { return "Invalid Number value for " + attributeName + "."; } else if ( dataType == B ) { return "Invalid Base64 string for " + attributeName + "."; } else { return ""; } } /** * Validates the user input of Base64 string. */ private static boolean validateBase64StringInput(String base64) { byte[] decoded = BinaryUtils.fromBase64(base64); String encodedAgain = BinaryUtils.toBase64(decoded); return base64.equals(encodedAgain); } /** * Validates the user input of a number. */ private static boolean validateNumberStringInput(String number) { try { new BigInteger(number, 10); } catch ( NumberFormatException e ) { return false; } return true; } }