package com.googlecode.objectify.impl.translate; import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.impl.Path; import com.googlecode.objectify.util.GenericUtils; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * <p>Translator which can load things into a collection field. Those things are themselves translated.</p> * * <p>This translator is clever about recycling an existing collection in the POJO field when loading. * That way a collection that has been initialized with a sort (or other data) will remain intact.</p> * * <p>Note that empty or null collections are not stored in the datastore, and null values for the collection * field are ignored when they are loaded from the Entity. This is because the datastore doesn't store empty * collections, and storing null fields will confuse filtering for actual nulls in the collection contents.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class CollectionTranslatorFactory implements TranslatorFactory<Collection<Object>, Collection<Object>> { @Override public Translator<Collection<Object>, Collection<Object>> create(TypeKey<Collection<Object>> tk, CreateContext ctx, Path path) { @SuppressWarnings("unchecked") final Class<? extends Collection<?>> collectionType = tk.getTypeAsClass(); if (!Collection.class.isAssignableFrom(collectionType)) return null; final ObjectifyFactory fact = ctx.getFactory(); Type componentType = GenericUtils.getCollectionComponentType(tk.getType()); final Translator<Object, Object> componentTranslator = ctx.getTranslator(new TypeKey<>(componentType, tk), ctx, path); return new TranslatorRecycles<Collection<Object>, Collection<Object>>() { @Override public Collection<Object> loadInto(Collection<Object> node, LoadContext ctx, Path path, Collection<Object> collection) throws SkipException { // If the collection does not exist, skip it entirely. This mirrors the underlying behavior // of collections in the datastore; if they are empty, they don't exist. if (node == null) throw new SkipException(); if (collection == null) //noinspection unchecked collection = (Collection<Object>)fact.constructCollection(collectionType, node.size()); else collection.clear(); for (Object child: node) { try { Object value = componentTranslator.load(child, ctx, path); collection.add(value); } catch (SkipException ex) { // No prob, just skip that one } } return collection; } @Override public Collection<Object> save(Collection<Object> pojo, boolean index, SaveContext ctx, Path path) throws SkipException { // If it's empty, might as well skip it - the datastore doesn't store empty lists if (pojo == null || pojo.isEmpty()) throw new SkipException(); List<Object> list = new ArrayList<>(); for (Object obj: pojo) { try { Object translatedChild = componentTranslator.save(obj, index, ctx, path); list.add(translatedChild); } catch (SkipException ex) { // Just skip that node, no prob } } return list; } }; } }