/**
* Copyright Microsoft Corporation
*
* 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://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License 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.microsoft.azure.storage.table;
import java.text.ParseException;
import java.util.Date;
import java.util.UUID;
import com.microsoft.azure.storage.Constants;
import com.microsoft.azure.storage.core.Base64;
import com.microsoft.azure.storage.core.SR;
import com.microsoft.azure.storage.core.Utility;
/**
* A class which represents a single typed property value in a table entity. An {@link EntityProperty} stores the data
* type as an {@link EdmType}. The value, which may be <code>null</code> for object types, but not for primitive types,
* is serialized and stored as a <code>String</code>.
* <p>
* {@link EntityProperty} provides overloaded constructors and overloads of the <code>setValue</code> method for
* supported value types. Each overloaded constructor or <code>setValue</code> method sets the {@link EdmType} and
* serializes the value appropriately based on the parameter type.
* <p>
* Use one of the <code>getValueAs</code><em>Type</em> methods to deserialize an {@link EntityProperty} as the
* appropriate type. The method will throw a {@link ParseException} or {@link IllegalArgumentException} if the
* {@link EntityProperty} cannot be deserialized as that type.
*/
public final class EntityProperty {
private String value;
private Class<?> type;
private EdmType edmType = EdmType.NULL;
/**
* Flag that specifies whether the client should look to correct Date values stored on a {@link TableEntity}
* that may have been written using versions of this library prior to 0.4.0.
* See <a href="http://go.microsoft.com/fwlink/?LinkId=523753">here</a> for more details.
*/
private boolean dateBackwardCompatibility = false;
/**
* Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>Object</code> value type,
* and verifies that the value can be interpreted as the specified data type.
*
* @param value
* The <code>Object</code> to convert to a string and store.
*/
protected EntityProperty(final String value, final Class<?> type) {
this.type = type;
this.value = value;
if (type.equals(byte[].class)) {
this.getValueAsByteArray();
this.edmType = EdmType.BINARY;
}
else if (type.equals(Byte[].class)) {
this.getValueAsByteObjectArray();
this.edmType = EdmType.BINARY;
}
else if (type.equals(String.class)) {
this.edmType = EdmType.STRING;
}
else if (type.equals(boolean.class)) {
this.getValueAsBoolean();
this.edmType = EdmType.BOOLEAN;
}
else if (type.equals(Boolean.class)) {
this.getValueAsBooleanObject();
this.edmType = EdmType.BOOLEAN;
}
else if (type.equals(Date.class)) {
this.getValueAsDate();
this.edmType = EdmType.DATE_TIME;
}
else if (type.equals(double.class)) {
this.getValueAsDouble();
this.edmType = EdmType.DOUBLE;
}
else if (type.equals(Double.class)) {
this.getValueAsDoubleObject();
this.edmType = EdmType.DOUBLE;
}
else if (type.equals(UUID.class)) {
this.getValueAsUUID();
this.edmType = EdmType.GUID;
}
else if (type.equals(int.class)) {
this.getValueAsInteger();
this.edmType = EdmType.INT32;
}
else if (type.equals(Integer.class)) {
this.getValueAsIntegerObject();
this.edmType = EdmType.INT32;
}
else if (type.equals(long.class)) {
this.getValueAsLong();
this.edmType = EdmType.INT64;
}
else if (type.equals(Long.class)) {
this.getValueAsLongObject();
this.edmType = EdmType.INT64;
}
else {
throw new IllegalArgumentException(String.format(SR.TYPE_NOT_SUPPORTED, type.toString()));
}
}
/**
* Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>Object</code> value and a
* data type, and verifies that the value can be interpreted as the specified data type.
*
* @param value
* The <code>Object</code> to convert to a string and store.
* @param edmType
* The <code>Class<?></code> type of the value to construct.
*/
protected EntityProperty(final Object value, final Class<?> type) {
if (type.equals(byte[].class)) {
setValue((byte[]) value);
this.type = type;
}
else if (type.equals(Byte[].class)) {
setValue((Byte[]) value);
this.type = type;
}
else if (type.equals(String.class)) {
setValue((String) value);
this.type = type;
}
else if (type.equals(boolean.class)) {
setValue(((Boolean) value).booleanValue());
this.type = type;
}
else if (type.equals(Boolean.class)) {
setValue((Boolean) value);
this.type = type;
}
else if (type.equals(double.class)) {
setValue(((Double) value).doubleValue());
this.type = type;
}
else if (type.equals(Double.class)) {
setValue((Double) value);
this.type = type;
}
else if (type.equals(UUID.class)) {
setValue((UUID) value);
this.type = type;
}
else if (type.equals(int.class)) {
setValue(((Integer) value).intValue());
this.type = type;
}
else if (type.equals(Integer.class)) {
setValue((Integer) value);
this.type = type;
}
else if (type.equals(long.class)) {
setValue(((Long) value).longValue());
this.type = type;
}
else if (type.equals(Long.class)) {
setValue((Long) value);
this.type = type;
}
else if (type.equals(Date.class)) {
setValue((Date) value);
this.type = type;
}
else {
throw new IllegalArgumentException(String.format(SR.TYPE_NOT_SUPPORTED, type.toString()));
}
}
/**
* Reserved for internal use. Constructs an {@link EntityProperty} instance from a <code>String</code> value and a
* data type, and verifies that the value can be interpreted as the specified data type.
*
* @param value
* The <code>String</code> representation of the value to construct.
* @param edmType
* The {@link EdmType} data type of the value to construct.
*/
protected EntityProperty(final String value, final EdmType edmType) {
this.edmType = edmType;
this.value = value;
// validate data is encoded correctly
if (edmType == EdmType.STRING) {
this.type = String.class;
return;
}
else if (edmType == EdmType.BINARY) {
this.getValueAsByteArray();
this.type = Byte[].class;
}
else if (edmType == EdmType.BOOLEAN) {
this.getValueAsBoolean();
this.type = Boolean.class;
}
else if (edmType == EdmType.DOUBLE) {
this.getValueAsDouble();
this.type = Double.class;
}
else if (edmType == EdmType.GUID) {
this.getValueAsUUID();
this.type = UUID.class;
}
else if (edmType == EdmType.INT32) {
this.getValueAsInteger();
this.type = Integer.class;
}
else if (edmType == EdmType.INT64) {
this.getValueAsLong();
this.type = Long.class;
}
else if (edmType == EdmType.DATE_TIME) {
this.getValueAsDate();
this.type = Date.class;
}
else {
// these are overwritten when this is called from the table parser with a more informative message
if (edmType == null) {
throw new IllegalArgumentException(SR.EDMTYPE_WAS_NULL);
}
throw new IllegalArgumentException(String.format(SR.INVALID_EDMTYPE_VALUE, edmType.toString()));
}
}
/**
* Constructs an {@link EntityProperty} instance from a <code>boolean</code> value.
*
* @param value
* The <code>boolean</code> value of the entity property to set.
*/
public EntityProperty(final boolean value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>Boolean</code> value.
*
* @param value
* The <code>Boolean</code> value of the entity property to set.
*/
public EntityProperty(final Boolean value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>byte[]</code> value.
*
* @param value
* The <code>byte[]</code> value of the entity property to set.
*/
public EntityProperty(final byte[] value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>Byte[]</code>.
*
* @param value
* The <code>Byte[]</code> to set as the entity property value.
*/
public EntityProperty(final Byte[] value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>java.util.Date</code> value.
*
* @param value
* The <code>java.util.Date</code> to set as the entity property value.
*/
public EntityProperty(final Date value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>double</code> value.
*
* @param value
* The <code>double</code> value of the entity property to set.
*/
public EntityProperty(final double value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>Double</code> value.
*
* @param value
* The <code>Double</code> value of the entity property to set.
*/
public EntityProperty(final Double value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from an <code>int</code> value.
*
* @param value
* The <code>int</code> value of the entity property to set.
*/
public EntityProperty(final int value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from an <code>Integer</code> value.
*
* @param value
* The <code>Integer</code> value of the entity property to set.
*/
public EntityProperty(final Integer value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>long</code> value.
*
* @param value
* The <code>long</code> value of the entity property to set.
*/
public EntityProperty(final long value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>Long</code> value.
*
* @param value
* The <code>Long</code> value of the entity property to set.
*/
public EntityProperty(final Long value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>String</code> value.
*
* @param value
* The <code>String</code> to set as the entity property value.
*/
public EntityProperty(final String value) {
this.setValue(value);
}
/**
* Constructs an {@link EntityProperty} instance from a <code>java.util.UUID</code> value.
*
* @param value
* The <code>java.util.UUID</code> to set as the entity property value.
*/
public EntityProperty(final UUID value) {
this.setValue(value);
}
/**
* Gets the {@link EdmType} storage data type for the {@link EntityProperty}.
*
* @return
* The {@link EdmType} enumeration value for the data type of the {@link EntityProperty}.
*/
public EdmType getEdmType() {
return this.edmType;
}
/**
* Gets a flag indicating that the {@link EntityProperty} value is <code>null</code>.
*
* @return
* A <code>boolean</code> flag indicating that the {@link EntityProperty} value is <code>null</code>.
*/
public boolean getIsNull() {
return this.value == null;
}
/**
* Gets the class type of the {@link EntityProperty}.
*
* @return
* The <code>Class<?></code> of the {@link EntityProperty}.
*/
public Class<?> getType() {
return this.type;
}
/**
* Gets the value of this {@link EntityProperty} as a <code>boolean</code>.
*
* @return
* A <code>boolean</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>Boolean</code>.
*/
public boolean getValueAsBoolean() {
if (this.getIsNull()) {
throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
}
return Boolean.parseBoolean(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>Boolean</code>.
*
* @return
* A <code>Boolean</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>Boolean</code>.
*/
public Boolean getValueAsBooleanObject() {
if (this.getIsNull()) {
return null;
}
return Boolean.parseBoolean(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>byte</code> array.
*
* @return
* A <code>byte[]</code> representation of the {@link EntityProperty} value, or <code>null</code>.
*/
public byte[] getValueAsByteArray() {
return this.getIsNull() ? null : Base64.decode(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>Byte</code> array.
*
* @return
* A <code>Byte[]</code> representation of the {@link EntityProperty} value, or <code>null</code>.
*/
public Byte[] getValueAsByteObjectArray() {
return this.getIsNull() ? null : Base64.decodeAsByteObjectArray(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>java.util.Date</code>.
*
* @return
* A <code>java.util.Date</code> representation of the {@link EntityProperty} value, or <code>null</code>.
*
* @throws IllegalArgumentException
* If the value is not <code>null</code> and cannot be parsed as a <code>java.util.Date</code>.
*/
public Date getValueAsDate() {
return this.getIsNull() ? null : Utility.parseDate(this.value, this.dateBackwardCompatibility);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>double</code>.
*
* @return
* A <code>double</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>double</code>.
*/
public double getValueAsDouble() {
if (this.getIsNull()) {
throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
}
else if (this.value.equals("Infinity") || this.value.equals("INF")) {
return Double.POSITIVE_INFINITY;
}
else if (this.value.equals("-Infinity") || this.value.equals("-INF")) {
return Double.NEGATIVE_INFINITY;
}
else if (this.value.equals("NaN")) {
return Double.NaN;
}
else {
return Double.parseDouble(this.value);
}
}
/**
* Gets the value of this {@link EntityProperty} as a <code>double</code>.
*
* @return
* A <code>double</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>double</code>.
*/
public Double getValueAsDoubleObject() {
if (this.getIsNull()) {
return null;
}
else if (this.value.equals("Infinity") || this.value.equals("INF")) {
return Double.POSITIVE_INFINITY;
}
else if (this.value.equals("-Infinity") || this.value.equals("-INF")) {
return Double.NEGATIVE_INFINITY;
}
else if (this.value.equals("NaN")) {
return Double.NaN;
}
else {
return Double.parseDouble(this.value);
}
}
/**
* Gets the value of this {@link EntityProperty} as an <code>int</code>.
*
* @return
* An <code>int</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as an <code>int</code>.
*/
public int getValueAsInteger() {
if (this.getIsNull()) {
throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
}
return Integer.parseInt(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as an <code>Integer</code>.
*
* @return
* An <code>Integer</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as an <code>int</code>.
*/
public Integer getValueAsIntegerObject() {
return this.getIsNull() ? null : Integer.parseInt(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>long</code>.
*
* @return
* A <code>long</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>long</code>.
*/
public long getValueAsLong() {
if (this.getIsNull()) {
throw new IllegalArgumentException(SR.ENTITY_PROPERTY_CANNOT_BE_NULL_FOR_PRIMITIVES);
}
return Long.parseLong(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>Long</code>.
*
* @return
* A <code>long</code> representation of the {@link EntityProperty} value.
*
* @throws IllegalArgumentException
* If the value is <code>null</code> or cannot be parsed as a <code>long</code>.
*/
public Long getValueAsLongObject() {
return this.getIsNull() ? null : Long.parseLong(this.value);
}
/**
* Gets the value of this {@link EntityProperty} as a <code>String</code>.
*
* @return
* A <code>String</code> representation of the {@link EntityProperty} value, or <code>null</code>.
*/
public String getValueAsString() {
return this.getIsNull() ? null : this.value;
}
/**
* Gets the value of this {@link EntityProperty} as a <code>java.util.UUID</code>.
*
* @return
* A <code>java.util.UUID</code> representation of the {@link EntityProperty} value, or <code>null</code>.
*
* @throws IllegalArgumentException
* If the value cannot be parsed as a <code>java.util.UUID</code>.
*/
public UUID getValueAsUUID() {
return this.getIsNull() ? null : UUID.fromString(this.value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>boolean</code> value.
*
* @param value
* The <code>boolean</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final boolean value) {
this.edmType = EdmType.BOOLEAN;
this.type = boolean.class;
this.value = value ? Constants.TRUE : Constants.FALSE;
}
/**
* Sets this {@link EntityProperty} using the serialized <code>Boolean</code> value.
*
* @param value
* The <code>Boolean</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final Boolean value) {
this.edmType = EdmType.BOOLEAN;
this.type = Boolean.class;
if (value == null) {
this.value = null;
}
else {
this.value = value ? Constants.TRUE : Constants.FALSE;
}
}
/**
* Sets this {@link EntityProperty} using the serialized <code>byte[]</code> value.
*
* @param value
* The <code>byte[]</code> value to set as the {@link EntityProperty} value. This value may be
* <code>null</code>.
*/
public synchronized final void setValue(final byte[] value) {
this.edmType = EdmType.BINARY;
this.type = byte[].class;
this.value = value == null ? null : Base64.encode(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>Byte[]</code> value.
*
* @param value
* The <code>Byte[]</code> value to set as the {@link EntityProperty} value. This value may be
* <code>null</code>.
*/
public synchronized final void setValue(final Byte[] value) {
this.edmType = EdmType.BINARY;
this.type = Byte[].class;
this.value = value == null ? null : Base64.encode(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>java.util.Date</code> value.
*
* @param value
* The <code>java.util.Date</code> value to set as the {@link EntityProperty} value. This value may be
* <code>null</code>.
*/
public synchronized final void setValue(final Date value) {
this.edmType = EdmType.DATE_TIME;
this.type = Date.class;
this.value = value == null ? null : Utility.getJavaISO8601Time(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>double</code> value.
*
* @param value
* The <code>double</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final double value) {
this.edmType = EdmType.DOUBLE;
this.type = double.class;
this.value = Double.toString(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>Double</code> value.
*
* @param value
* The <code>Double</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final Double value) {
this.edmType = EdmType.DOUBLE;
this.type = Double.class;
this.value = value == null ? null : Double.toString(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>int</code> value.
*
* @param value
* The <code>int</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final int value) {
this.edmType = EdmType.INT32;
this.type = int.class;
this.value = Integer.toString(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>Integer</code> value.
*
* @param value
* The <code>Integer</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final Integer value) {
this.edmType = EdmType.INT32;
this.type = Integer.class;
this.value = value == null ? null : Integer.toString(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>long</code> value.
*
* @param value
* The <code>long</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final long value) {
this.edmType = EdmType.INT64;
this.type = long.class;
this.value = Long.toString(value);
}
/**
* Sets this {@link EntityProperty} using the serialized <code>Long</code> value.
*
* @param value
* The <code>Long</code> value to set as the {@link EntityProperty} value.
*/
public synchronized final void setValue(final Long value) {
this.edmType = EdmType.INT64;
this.type = Long.class;
this.value = value == null ? null : Long.toString(value);
}
/**
* Sets this {@link EntityProperty} using the <code>String</code> value.
*
* @param value
* The <code>String</code> value to set as the {@link EntityProperty} value. This value may be
* <code>null</code>.
*/
public synchronized final void setValue(final String value) {
this.edmType = EdmType.STRING;
this.type = String.class;
this.value = value;
}
/**
* Sets this {@link EntityProperty} using the serialized <code>java.util.UUID</code> value.
*
* @param value
* The <code>java.util.UUID</code> value to set as the {@link EntityProperty} value.
* This value may be <code>null</code>.
*/
public synchronized final void setValue(final UUID value) {
this.edmType = EdmType.GUID;
this.type = UUID.class;
this.value = value == null ? null : value.toString();
}
/**
* Sets whether the client should look to correct Date values stored on a {@link TableEntity}
* that may have been written using versions of this library prior to 0.4.0.
* <p>
* {@link #dateBackwardCompatibility} is by default <code>false</code>, indicating a post 0.4.0 version or
* mixed-platform usage.
* <p>
* See <a href="http://go.microsoft.com/fwlink/?LinkId=523753">here</a> for more details.
*
* @param dateBackwardCompatibility
* <code>true</code> to enable <code>dateBackwardCompatibility</code>; otherwise, <code>false</code>
*/
void setDateBackwardCompatibility(boolean dateBackwardCompatibility) {
this.dateBackwardCompatibility = dateBackwardCompatibility;
}
}