/*
* Copyright (c) 2011-2015 Spotify AB
*
* 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.spotify.asyncdatastoreclient;
import com.google.common.collect.Maps;
import com.google.protobuf.ByteString;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
/**
* Represents an entity that is stored in Datastore.
*
* All properties are immutable; use {@code Entity.builder()} to construct new
* {@code Entity} instances.
*/
public final class Entity {
private final com.google.datastore.v1.Entity entity;
private Map<String, Value> properties;
private Entity(final com.google.datastore.v1.Entity entity) {
this.entity = entity;
this.properties = null;
}
public static final class Builder {
private com.google.datastore.v1.Entity.Builder entity;
private Map<String, Value> properties;
private Builder() {
this.entity = com.google.datastore.v1.Entity.newBuilder();
this.properties = Maps.newHashMap();
}
private Builder(final Key key) {
this.entity = com.google.datastore.v1.Entity.newBuilder().setKey(key.getPb());
this.properties = Maps.newHashMap();
}
private Builder(final Entity entity) {
this(entity.getPb());
}
private Builder(final com.google.datastore.v1.Entity entity) {
this.entity = com.google.datastore.v1.Entity.newBuilder(entity);
this.properties = entity.getProperties().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> Value.builder(e.getValue()).build()));
}
/**
* Creates a new {@code Entity}.
*
* @return an immutable entity.
*/
public Entity build() {
entity.getMutableProperties().clear();
entity.putAllProperties(
properties
.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey, e -> e.getValue().getPb().toBuilder().build())));
return new Entity(entity.build());
}
/**
* Set the key for this entity.
*
* @param key the key to set for this entity.
* @return this entity builder.
*/
public Builder key(final Key key) {
entity.setKey(key.getPb());
return this;
}
/**
* Set property and its value for this entity.
*
* @param name the property name to set.
* @param value the property value.
* @return this entity builder.
*/
public Builder property(final String name, final Value value) {
properties.put(name, value);
return this;
}
/**
* Set property and its value for this entity.
*
* @param name the property name to set.
* @param value the property value.
* @return this entity builder.
*/
public Builder property(final String name, final Object value) {
properties.put(name, Value.builder(value).build());
return this;
}
/**
* Set property and its value for this entity.
*
* @param name the property name to set.
* @param value the property value.
* @param indexed indicates whether the value should be indexed or not.
* @return this entity builder.
*/
public Builder property(final String name, final Object value, final boolean indexed) {
properties.put(name, Value.builder(value).indexed(indexed).build());
return this;
}
/**
* Set property and a list of value for this entity.
*
* @param name the property name to set.
* @param values a list of value.
* @return this entity builder.
*/
public Builder property(final String name, final List<Object> values) {
properties.put(name, Value.builder(values).build());
return this;
}
/**
* Remove a property from this entity.
*
* @param name the property name to remove.
* @return this entity builder.
*/
public Builder remove(final String name) {
properties.remove(name);
return this;
}
}
/**
* Creates a new empty {@code Entity} builder.
*
* @return an entity builder.
*/
public static Entity.Builder builder() {
return new Entity.Builder();
}
/**
* Creates a new {@code Entity} builder for a given kind.
*
* This is a shortcut for {@code Entity.builder().key(Key.builder(kind).build())}
*
* @param kind the kind of entity.
* @return an entity builder.
*/
public static Entity.Builder builder(final String kind) {
return new Entity.Builder(Key.builder(kind).build());
}
/**
* Creates a new {@code Entity} builder for a given kind and key id.
*
* This is a shortcut for {@code Entity.builder().key(Key.builder(kind, id).build())}
*
* @param kind the kind of entity.
* @param id the key id.
* @return an entity builder.
*/
public static Entity.Builder builder(final String kind, final long id) {
return new Entity.Builder(Key.builder(kind, id).build());
}
/**
* Creates a new {@code Entity} builder for a given kind and key name.
*
* This is a shortcut for {@code Entity.builder().key(Key.builder(kind, name).build())}
*
* @param kind the kind of entity.
* @param name the key name.
* @return an entity builder.
*/
public static Entity.Builder builder(final String kind, final String name) {
return new Entity.Builder(Key.builder(kind, name).build());
}
/**
* Creates a new {@code Entity} builder for a given key.
*
* This is a shortcut for {@code Entity.builder().key(key).build())}
*
* @param key the key for this entity.
* @return an entity builder.
*/
public static Entity.Builder builder(final Key key) {
return new Entity.Builder(key);
}
/**
* Creates a new {@code Entity} builder based on an existing entity.
*
* @param entity the entity to use as a base.
* @return an entity builder.
*/
public static Entity.Builder builder(final Entity entity) {
return new Entity.Builder(entity);
}
static Entity.Builder builder(final com.google.datastore.v1.Entity entity) {
return new Entity.Builder(entity);
}
/**
* Return the key for this entity.
*
* @return a {@code Key}.
*/
public Key getKey() {
return Key.builder(entity.getKey()).build();
}
/**
* Return the value for a given property as a string, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public String getString(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getString();
}
/**
* Return the value for a given property as an integer, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Long getInteger(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getInteger();
}
/**
* Return the value for a given property as a boolean, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Boolean getBoolean(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getBoolean();
}
/**
* Return the value for a given property as a double, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Double getDouble(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getDouble();
}
/**
* Return the value for a given property as a date, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Date getDate(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getDate();
}
/**
* Return the value for a given property as a blob, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public ByteString getBlob(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getBlob();
}
/**
* Return the value for a given property as an entity, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Entity getEntity(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getEntity();
}
/**
* Return the value for a given property as a key, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a property value.
*/
public Key getKey(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getKey();
}
/**
* Return the value for a given property as a list of {@code Key}, or null
* if the property doesn't exist.
*
* @param name the name of the property to get.
* @return a list of property values.
*/
public List<Value> getList(final String name) {
final Value value = getProperties().get(name);
return value == null ? null : value.getList();
}
/**
* Return the value for a given property as a list of values that are cast
* to a given type, or null if the property doesn't exist.
*
* @param name the name of the property to get.
* @param clazz the type of class to cast values to.
* @return a list of property values.
*/
public <T> List<T> getList(final String name, final Class<T> clazz) {
final Value value = getProperties().get(name);
return value == null ? null : value.getList(clazz);
}
/**
* Return a map of properties to their values for this entity.
*
* @return a map of property values.
*/
public Map<String, Value> getProperties() {
if (properties == null) {
properties = entity
.getProperties()
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> Value.builder(e.getValue()).build()));
}
return properties;
}
/**
* Get the value for given property, or empty if none exists.
*
* @param name name of the property to get.
* @return an optional containing a property value, or empty.
*/
public Optional<Value> get(final String name) {
return Optional.ofNullable(getProperties().get(name));
}
/**
* Return whether a given property name exists in this entity.
*
* @param name the name of the property.
* @return true if the property exists.
*/
public boolean contains(final String name) {
return getProperties().containsKey(name);
}
@Override
public String toString() {
return "{" + getProperties().entrySet().stream()
.map(property -> property.getKey() + ":" + property.getValue())
.collect(Collectors.joining(", ")) + "}";
}
@Override
public int hashCode() {
return entity.hashCode();
}
@Override
public boolean equals(final Object obj) {
return obj.getClass() == this.getClass() || (obj instanceof Entity && Objects.equals(entity, ((Entity) obj).entity));
}
com.google.datastore.v1.Entity getPb() {
return entity;
}
com.google.datastore.v1.Entity getPb(final String namespace) {
final Map<String, com.google.datastore.v1.Value> propertiesLocal = entity.getProperties()
.entrySet()
.stream()
.collect(Collectors.toMap(
Map.Entry::getKey, e -> Value.builder(e.getValue()).build().getPb(namespace)));
return com.google.datastore.v1.Entity
.newBuilder()
.putAllProperties(propertiesLocal)
.setKey(getKey().getPb(namespace))
.build();
}
}