Package org.slim3.datastore

Source Code of org.slim3.datastore.DatastoreUtil

/*
* 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.Path.Element;
import com.google.storage.onestore.v3.OnestoreEntity.Reference;

/**
* 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();
    }

    /**
     * Clears the keys cache.
     */
    public static void clearKeysCache() {
        keysCache.clear();
    }

    /**
     * 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() {
    }
}
TOP

Related Classes of org.slim3.datastore.DatastoreUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.