/*
* Copyright 2004-2010 the Seasar Foundation and the Others.
*
* 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 org.slim3.datastore;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Future;
import org.slim3.util.ClassUtil;
import org.slim3.util.Cleanable;
import org.slim3.util.Cleaner;
import org.slim3.util.FutureUtil;
import com.google.appengine.api.datastore.AsyncDatastoreService;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityTranslator;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.KeyRange;
import com.google.appengine.api.datastore.KeyUtil;
import com.google.appengine.api.datastore.Transaction;
import com.google.storage.onestore.v3.OnestoreEntity.EntityProto;
import com.google.storage.onestore.v3.OnestoreEntity.Reference;
import com.google.storage.onestore.v3.OnestoreEntity.Path.Element;
/**
* A utility for {@link DatastoreService}.
*
* @author higa
* @since 1.0.0
*
*/
public final class DatastoreUtil {
/**
* The maximum size(bytes) of entity.
*/
public static final int MAX_ENTITY_SIZE = 1000000;
/**
* The maximum number of entities.
*/
public static final int MAX_NUMBER_OF_ENTITIES = 500;
/**
* The extra size.
*/
public static final int EXTRA_SIZE = 200;
private static final int KEY_CACHE_SIZE = 50;
/**
* The cache for {@link ModelMeta}.
*/
protected static ConcurrentHashMap<String, ModelMeta<?>> modelMetaCache =
new ConcurrentHashMap<String, ModelMeta<?>>(87);
/**
* The cache for the result of allocateIds().
*/
protected static ConcurrentHashMap<String, Iterator<Key>> keysCache =
new ConcurrentHashMap<String, Iterator<Key>>(87);
private static volatile boolean initialized = false;
static {
initialize();
}
private static void initialize() {
Cleaner.add(new Cleanable() {
public void clean() {
modelMetaCache.clear();
initialized = false;
}
});
initialized = true;
}
/**
* Clears the active transactions.
*/
public static void clearActiveGlobalTransactions() {
GlobalTransaction.clearActiveTransactions();
}
/**
* Begins a transaction.
*
* @param ds
* the asynchronous datastore service
* @return a begun transaction
*/
public static Transaction beginTransaction(AsyncDatastoreService ds) {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
return FutureUtil.getQuietly(ds.beginTransaction());
}
/**
* Allocates a key within a namespace defined by the kind with caching.
*
* @param ds
* the asynchronous datastore service
* @param kind
* the kind
* @return a key within a namespace defined by the kind
* @throws NullPointerException
* if the ds parameter is null or if the kind parameter is null
*/
public static Key allocateId(AsyncDatastoreService ds, String kind)
throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (kind == null) {
throw new NullPointerException(
"The kind parameter must not be null.");
}
Iterator<Key> keys = keysCache.get(kind);
if (keys != null && keys.hasNext()) {
return keys.next();
}
keys =
FutureUtil
.getQuietly(allocateIdsAsync(ds, kind, KEY_CACHE_SIZE))
.iterator();
keysCache.put(kind, keys);
return keys.next();
}
/**
* Allocates a key within a namespace defined by the parentKey and the kind.
*
* @param ds
* the asynchronous datastore service
* @param parentKey
* the parent key
* @param kind
* the kind
* @return a key within a namespace defined by the kind
* @throws NullPointerException
* if the ds parameter is null or if the parentKey parameter is
* null or if the kind parameter is null
*/
public static Key allocateId(AsyncDatastoreService ds, Key parentKey,
String kind) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (parentKey == null) {
throw new NullPointerException(
"The parentKey parameter must not be null.");
}
if (kind == null) {
throw new NullPointerException(
"The kind parameter must not be null.");
}
Iterator<Key> keys =
FutureUtil
.getQuietly(allocateIdsAsync(ds, parentKey, kind, 1))
.iterator();
return keys.next();
}
/**
* Allocates keys within a namespace defined by the kind asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param kind
* the kind
* @param num
* the number of allocated keys
* @return keys represented as {@link Future}
* @throws NullPointerException
* if the ads parameter is null or if the kind parameter is null
*/
public static Future<KeyRange> allocateIdsAsync(AsyncDatastoreService ds,
String kind, long num) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (kind == null) {
throw new NullPointerException(
"The kind parameter must not be null.");
}
return ds.allocateIds(kind, num);
}
/**
* Allocates keys within a namespace defined by the parent key and the kind
* asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param parentKey
* the parent key
* @param kind
* the kind
* @param num
* @return keys represented as {@link Future}
* @throws NullPointerException
* if the ds parameter is null or if the parentKey parameter is
* null or if the kind parameter is null
*/
public static Future<KeyRange> allocateIdsAsync(AsyncDatastoreService ds,
Key parentKey, String kind, long num) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (parentKey == null) {
throw new NullPointerException(
"The parentKey parameter must not be null.");
}
if (kind == null) {
throw new NullPointerException("The kind parameter is null.");
}
return ds.allocateIds(parentKey, kind, num);
}
/**
* Assigns a new key to the entity if necessary.
*
* @param ds
* the asynchronous datastore service
* @param entity
* the entity
* @throws NullPointerException
* if the ds parameter is null or if the entity parameter is
* null
*/
public static void assignKeyIfNecessary(AsyncDatastoreService ds,
Entity entity) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (entity == null) {
throw new NullPointerException(
"The entity parameter must not be null.");
}
if (!entity.getKey().isComplete()) {
long id =
entity.getParent() == null ? allocateId(ds, entity.getKind())
.getId() : allocateId(
ds,
entity.getParent(),
entity.getKind()).getId();
KeyUtil.setId(entity.getKey(), id);
}
}
/**
* Assigns a new key to the entity if necessary.
*
* @param ds
* the asynchronous datastore service
* @param entities
* the entities
* @throws NullPointerException
* if the ds parameter is null or if the entities parameter is
* null
*/
public static void assignKeyIfNecessary(AsyncDatastoreService ds,
Iterable<Entity> entities) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (entities == null) {
throw new NullPointerException(
"The entities parameter must not be null.");
}
for (Entity e : entities) {
assignKeyIfNecessary(ds, e);
}
}
/**
* Returns entities specified by the keys as map within the provided
* transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param key
* the key
* @return an entity
* @throws NullPointerException
* if the ds parameter is null or if the key parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
* @throws EntityNotFoundRuntimeException
* if no entity is found
*/
public static Entity get(AsyncDatastoreService ds, Transaction tx, Key key)
throws NullPointerException, IllegalStateException,
EntityNotFoundRuntimeException {
Entity entity = getOrNull(ds, tx, key);
if (entity == null) {
throw new EntityNotFoundRuntimeException(key);
}
return entity;
}
/**
* Returns entities specified by the keys as map within the provided
* transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param key
* the key
* @return an entity
* @throws NullPointerException
* if the ds parameter is null or if the key parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Entity getOrNull(AsyncDatastoreService ds, Transaction tx,
Key key) throws NullPointerException, IllegalStateException {
if (key == null) {
throw new NullPointerException(
"The key parameter must not be null.");
}
return FutureUtil
.getQuietly(getAsMapAsync(ds, tx, Arrays.asList(key)))
.get(key);
}
/**
* Returns entities specified by the keys as map within the provided
* transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param keys
* the keys
* @return entities
* @throws NullPointerException
* if the ds parameter is null or if the keys parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Map<Key, Entity> getAsMap(AsyncDatastoreService ds,
Transaction tx, Iterable<Key> keys) throws NullPointerException,
IllegalStateException {
return FutureUtil.getQuietly(getAsMapAsync(ds, tx, keys));
}
/**
* Returns entities specified by the keys as map within the provided
* transaction asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param keys
* the keys
* @return entities represented as {@link Future}
* @throws NullPointerException
* if the ds parameter is null or if the keys parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Future<Map<Key, Entity>> getAsMapAsync(
AsyncDatastoreService ds, Transaction tx, Iterable<Key> keys)
throws NullPointerException, IllegalStateException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (keys == null) {
throw new NullPointerException(
"The keys parameter must not be null.");
}
if (tx != null && !tx.isActive()) {
throw new IllegalStateException("The transaction must be active.");
}
return ds.get(tx, keys);
}
/**
* Puts the entity to datastore within the provided transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param entity
* the entity
*
* @return a list of keys
* @throws NullPointerException
* if the ds parameter is null or if the entity parameter is
* null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Key put(AsyncDatastoreService ds, Transaction tx,
Entity entity) throws NullPointerException, IllegalStateException {
if (entity == null) {
throw new NullPointerException(
"The entity parameter must not be null.");
}
List<Key> list = put(ds, tx, Arrays.asList(entity));
return list.get(0);
}
/**
* Puts the entities to datastore within the provided transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param entities
* the entities
*
* @return a list of keys
* @throws NullPointerException
* if the ds parameter is null or if the entities parameter is
* null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static List<Key> put(AsyncDatastoreService ds, Transaction tx,
Iterable<Entity> entities) throws NullPointerException,
IllegalStateException {
return FutureUtil.getQuietly(putAsync(ds, tx, entities));
}
/**
* Puts the entity to datastore within the provided transaction
* asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param entity
* the entity
*
* @return a list of keys represented as {@link Future}
* @throws NullPointerException
* if the ds parameter is null or if the entity parameter is
* null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Key putAsync(AsyncDatastoreService ds, Transaction tx,
Entity entity) throws NullPointerException, IllegalStateException {
if (entity == null) {
throw new NullPointerException(
"The entity parameter must not be null.");
}
List<Key> list = put(ds, tx, Arrays.asList(entity));
return list.get(0);
}
/**
* Puts the entities to datastore within the provided transaction
* asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param entities
* the entities
*
* @return a list of keys represented as {@link Future}
* @throws NullPointerException
* if the ds parameter is null or if the entities parameter is
* null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Future<List<Key>> putAsync(AsyncDatastoreService ds,
Transaction tx, Iterable<Entity> entities)
throws NullPointerException, IllegalStateException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (entities == null) {
throw new NullPointerException(
"The entities parameter must not be null.");
}
if (tx != null && !tx.isActive()) {
throw new IllegalStateException("The transaction must be active.");
}
assignKeyIfNecessary(ds, entities);
return ds.put(tx, entities);
}
/**
* Deletes the entity specified by the key within the provided transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param key
* the key
* @throws NullPointerException
* if the ds parameter is null or if the key parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static void delete(AsyncDatastoreService ds, Transaction tx, Key key)
throws NullPointerException, IllegalStateException {
if (key == null) {
throw new NullPointerException(
"The key parameter must not be null.");
}
FutureUtil.getQuietly(deleteAsync(ds, tx, Arrays.asList(key)));
}
/**
* Deletes entities specified by the keys within the provided transaction.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param keys
* the keys
* @throws NullPointerException
* if the ds parameter is null or if the keys parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static void delete(AsyncDatastoreService ds, Transaction tx,
Iterable<Key> keys) throws NullPointerException,
IllegalStateException {
FutureUtil.getQuietly(deleteAsync(ds, tx, keys));
}
/**
* Deletes entities specified by the keys within the provided transaction
* asynchronously.
*
* @param ds
* the asynchronous datastore service
* @param tx
* the transaction
* @param keys
* the keys
* @return a {@link Void} represented as {@link Future}
* @throws NullPointerException
* if the ds parameter is null or if the keys parameter is null
* @throws IllegalStateException
* if the transaction is not null and the transaction is not
* active
*/
public static Future<Void> deleteAsync(AsyncDatastoreService ds,
Transaction tx, Iterable<Key> keys) throws NullPointerException,
IllegalStateException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (keys == null) {
throw new NullPointerException(
"The keys parameter must not be null.");
}
if (tx != null && !tx.isActive()) {
throw new IllegalStateException("The transaction must be active.");
}
return ds.delete(tx, keys);
}
/**
* Filters the list in memory.
*
* @param <M>
* the model type
* @param list
* the model list
* @param criteria
* the filter criteria
* @return the filtered list.
* @throws NullPointerException
* if the list parameter is null or if the criteria parameter is
* null or if the model of the list is null
*/
public static <M> List<M> filterInMemory(List<M> list,
List<? extends InMemoryFilterCriterion> criteria)
throws NullPointerException {
if (list == null) {
throw new NullPointerException(
"The list parameter must not be null.");
}
if (criteria == null) {
throw new NullPointerException(
"The criteria parameter must not be null.");
}
if (criteria.size() == 0) {
return list;
}
List<M> newList = new ArrayList<M>(list.size());
for (M model : list) {
if (model == null) {
throw new NullPointerException(
"The element of list must not be null.");
}
if (accept(model, criteria)) {
newList.add(model);
}
}
return newList;
}
private static boolean accept(Object model,
List<? extends InMemoryFilterCriterion> criteria) {
for (InMemoryFilterCriterion c : criteria) {
if (c == null) {
continue;
}
if (!c.accept(model)) {
return false;
}
}
return true;
}
/**
* Sorts the list in memory.
*
* @param <M>
* the model type
* @param list
* the model list
* @param criteria
* criteria to sort
* @return the sorted list
* @throws NullPointerException
* if the list parameter is null of if the criteria parameter is
* null
*/
public static <M> List<M> sortInMemory(List<M> list,
List<InMemorySortCriterion> criteria) throws NullPointerException {
if (list == null) {
throw new NullPointerException(
"The list parameter must not be null.");
}
if (criteria == null) {
throw new NullPointerException(
"The criteria parameter must not be null.");
}
if (criteria.size() == 0) {
return list;
}
Collections.sort(list, new AttributeComparator(criteria));
return list;
}
/**
* Returns a meta data of the model
*
* @param <M>
* the model type
* @param modelClass
* the model class
* @return a meta data of the model
* @throws NullPointerException
* if the modelClass parameter is null
*/
@SuppressWarnings("unchecked")
public static <M> ModelMeta<M> getModelMeta(Class<M> modelClass)
throws NullPointerException {
if (modelClass == null) {
throw new NullPointerException(
"The modelClass parameter must not be null.");
}
if (!initialized) {
initialize();
}
ModelMeta<M> modelMeta =
(ModelMeta<M>) modelMetaCache.get(modelClass.getName());
if (modelMeta != null) {
return modelMeta;
}
modelMeta = createModelMeta(modelClass);
ModelMeta<?> old =
modelMetaCache.putIfAbsent(modelClass.getName(), modelMeta);
return old != null ? (ModelMeta<M>) old : modelMeta;
}
/**
* Returns a meta data of the model
*
* @param <M>
* the model type
* @param modelMeta
* the meta data of model
* @param entity
* the entity
* @return a meta data of the model
* @throws NullPointerException
* if the modelMeta parameter is null or if the entity parameter
* is null
* @throws IllegalArgumentException
* if the model class is not assignable from entity class
*/
@SuppressWarnings("unchecked")
public static <M> ModelMeta<M> getModelMeta(ModelMeta<M> modelMeta,
Entity entity) throws NullPointerException,
IllegalArgumentException {
if (modelMeta == null) {
throw new NullPointerException(
"The modelMeta parameter must not be null.");
}
if (entity == null) {
throw new NullPointerException(
"The entity parameter must not be null.");
}
List<String> classHierarchyList =
(List<String>) entity.getProperty(modelMeta
.getClassHierarchyListName());
if (classHierarchyList == null) {
return modelMeta;
}
Class<M> subModelClass =
ClassUtil.forName(classHierarchyList
.get(classHierarchyList.size() - 1));
if (!modelMeta.getModelClass().isAssignableFrom(subModelClass)) {
throw new IllegalArgumentException("The model class("
+ modelMeta.getModelClass().getName()
+ ") is not assignable from entity class("
+ subModelClass.getName()
+ ").");
}
return getModelMeta(subModelClass);
}
/**
* Creates a meta data of the model
*
* @param <M>
* the model type
* @param modelClass
* the model class
* @return a meta data of the model
*/
public static <M> ModelMeta<M> createModelMeta(Class<M> modelClass) {
try {
String className = modelClass.getName();
className = replacePackageName(className, "model", "meta");
className =
replacePackageName(className, "shared", "server") + "Meta";
return ClassUtil.newInstance(className, Thread
.currentThread()
.getContextClassLoader());
} catch (Throwable cause) {
throw new IllegalArgumentException("The meta data of the model("
+ modelClass.getName()
+ ") is not found.");
}
}
/**
* Replaces a package name with another one.
*
* @param className
* the class name
* @param fromPackageName
* the "from" package name
* @param toPackageName
* the "to" package name
* @return the converted class name
* @throws NullPointerException
* if the className parameter is null or if the fromPackageName
* is null or if the toPackageName parameter is null
*/
protected static String replacePackageName(String className,
String fromPackageName, String toPackageName)
throws NullPointerException {
if (className == null) {
throw new NullPointerException(
"The className parameter must not be null.");
}
if (fromPackageName == null) {
throw new NullPointerException(
"The fromPackageName parameter must not be null.");
}
if (toPackageName == null) {
throw new NullPointerException(
"The toPackageName parameter must not be null.");
}
int index = className.lastIndexOf("." + fromPackageName + ".");
if (index < 0) {
return className;
}
return className.substring(0, index)
+ "."
+ toPackageName
+ "."
+ className.substring(index + fromPackageName.length() + 2);
}
/**
* Converts the entity to an array of bytes.
*
* @param entity
* the entity
* @return an array of bytes
* @throws NullPointerException
* if the entity parameter is null
*/
public static byte[] entityToBytes(Entity entity)
throws NullPointerException {
if (entity == null) {
throw new NullPointerException(
"The entity parameter must not be null.");
}
EntityProto pb = EntityTranslator.convertToPb(entity);
byte[] buf = new byte[pb.encodingSize()];
pb.outputTo(buf, 0);
return buf;
}
/**
* Converts the array of bytes to an entity.
*
* @param bytes
* the array of bytes
* @return an entity
* @throws NullPointerException
* if the bytes parameter is null
*/
public static Entity bytesToEntity(byte[] bytes)
throws NullPointerException {
if (bytes == null) {
throw new NullPointerException(
"The bytes parameter must not be null.");
}
EntityProto pb = new EntityProto();
pb.mergeFrom(bytes);
return EntityTranslator.createFromPb(pb);
}
/**
* Converts the reference to a key.
*
* @param reference
* the reference object
* @return a key
* @throws NullPointerException
* if the reference parameter is null
*/
public static Key referenceToKey(Reference reference)
throws NullPointerException {
if (reference == null) {
throw new NullPointerException(
"The reference parameter must not be null.");
}
Key key = null;
for (Element e : reference.getPath().elements()) {
String kind = e.getType();
long id = e.getId();
String name = e.getName();
if (key == null) {
if (id != 0) {
key = KeyFactory.createKey(kind, id);
} else {
key = KeyFactory.createKey(kind, name);
}
} else {
if (id != 0) {
key = KeyFactory.createKey(key, kind, id);
} else {
key = KeyFactory.createKey(key, kind, name);
}
}
}
if (key == null) {
throw new IllegalArgumentException("The reference("
+ reference
+ ") cannot be converted to Key.");
}
return key;
}
/**
* Converts the model to an entity.
*
* @param ds
* the asynchronous datastore service
* @param model
* the model
* @return an entity
* @throws NullPointerException
* if the ds parameter is null or if the model parameter is null
*/
public static Entity modelToEntity(AsyncDatastoreService ds, Object model)
throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (model == null) {
throw new NullPointerException(
"The model parameter must not be null.");
}
ModelMeta<?> modelMeta = getModelMeta(model.getClass());
Key key = modelMeta.getKey(model);
if (key == null) {
key = allocateId(ds, modelMeta.getKind());
modelMeta.setKey(model, key);
}
modelMeta.assignKeyToModelRefIfNecessary(ds, model);
modelMeta.incrementVersion(model);
modelMeta.prePut(model);
return modelMeta.modelToEntity(model);
}
/**
* Converts the models to entities.
*
* @param ds
* the asynchronous datastore service
* @param models
* the models
* @return entities
* @throws NullPointerException
* if the ds parameter is null or if the models parameter is
* null
*/
public static List<Entity> modelsToEntities(AsyncDatastoreService ds,
Iterable<?> models) throws NullPointerException {
if (ds == null) {
throw new NullPointerException("The ds parameter must not be null.");
}
if (models == null) {
throw new NullPointerException(
"The models parameter must not be null.");
}
List<Entity> entities = new ArrayList<Entity>();
for (Object model : models) {
if (model instanceof Entity) {
Entity entity = (Entity) model;
assignKeyIfNecessary(ds, entity);
entities.add(entity);
} else {
entities.add(modelToEntity(ds, model));
}
}
return entities;
}
/**
* Converts the map of entities to a list of entities.
*
* @param keys
* the keys
* @param map
* the map of entities
* @return a list of entities
* @throws NullPointerException
* if the keys parameter is null or if the map parameter is null
* or if the element of keys is null
* @throws EntityNotFoundRuntimeException
* if no entity bound to a key is found
*/
public static List<Entity> entityMapToEntityList(Iterable<Key> keys,
Map<Key, Entity> map) throws NullPointerException,
EntityNotFoundRuntimeException {
if (keys == null) {
throw new NullPointerException(
"The keys parameter must not be null.");
}
if (map == null) {
throw new NullPointerException(
"The map parameter must not be null.");
}
List<Entity> list = new ArrayList<Entity>(map.size());
for (Key key : keys) {
if (key == null) {
throw new NullPointerException(
"The element of keys must not be null.");
}
Entity entity = map.get(key);
if (entity == null) {
throw new EntityNotFoundRuntimeException(key);
}
list.add(entity);
}
return list;
}
/**
* Converts the map of entities to a list of models.
*
* @param <M>
* the model type
* @param modelMeta
* the meta data of model
* @param keys
* the keys
* @param map
* the map of entities
* @return a list of models
* @throws NullPointerException
* if the modelMeta parameter is null or if the keys parameter
* is null or if the map parameter is null or if the element of
* keys is null
* @throws EntityNotFoundRuntimeException
* if no entity bound to a key is found
*/
public static <M> List<M> entityMapToModelList(ModelMeta<M> modelMeta,
Iterable<Key> keys, Map<Key, Entity> map)
throws NullPointerException, EntityNotFoundRuntimeException {
if (modelMeta == null) {
throw new NullPointerException(
"The modelMeta parameter must not be null.");
}
if (keys == null) {
throw new NullPointerException(
"The keys parameter must not be null.");
}
if (map == null) {
throw new NullPointerException(
"The map parameter must not be null.");
}
List<M> list = new ArrayList<M>(map.size());
for (Key key : keys) {
if (key == null) {
throw new NullPointerException(
"The element of keys must not be null.");
}
Entity entity = map.get(key);
if (entity == null) {
throw new EntityNotFoundRuntimeException(key);
}
ModelMeta<M> mm = getModelMeta(modelMeta, entity);
mm.validateKey(key);
M model = mm.entityToModel(entity);
mm.postGet(model);
list.add(model);
}
return list;
}
/**
* Converts the map of entities to a map of models.
*
* @param <M>
* the model type
* @param modelMeta
* the meta data of model
* @param map
* the map of entities
* @return a map of models
* @throws NullPointerException
* if the modelMeta parameter is null or if the map parameter is
* null
*/
public static <M> Map<Key, M> entityMapToModelMap(ModelMeta<M> modelMeta,
Map<Key, Entity> map) throws NullPointerException {
if (modelMeta == null) {
throw new NullPointerException(
"The modelMeta parameter must not be null.");
}
if (map == null) {
throw new NullPointerException(
"The map parameter must not be null.");
}
Map<Key, M> modelMap = new HashMap<Key, M>(map.size());
for (Key key : map.keySet()) {
Entity entity = map.get(key);
ModelMeta<M> mm = getModelMeta(modelMeta, entity);
mm.validateKey(key);
M model = mm.entityToModel(entity);
mm.postGet(model);
modelMap.put(key, model);
}
return modelMap;
}
/**
* Returns a root key.
*
* @param key
* the key
* @return a root key
*/
public static Key getRoot(Key key) {
if (key == null) {
throw new NullPointerException(
"The key parameter must not be null.");
}
while (key != null) {
Key parent = key.getParent();
if (parent == null) {
break;
}
key = parent;
}
return key;
}
private DatastoreUtil() {
}
}