package com.googlecode.objectify.impl.translate; import com.googlecode.objectify.impl.Path; import com.googlecode.objectify.repackaged.gentyref.GenericTypeReflector; import java.lang.reflect.Array; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * <p>Translator which can load an array of things.</p> * * <p>Note that empty or null arrays are not stored in the datastore, and null values for the array * 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 array contents.</p> * * <p>The reason the generic P type of this factory is Object instead of Object[] is that Object[] * is incompatible with the primitive arrays. This factory handles primitives as well.</p> * * @see CollectionTranslatorFactory * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class ArrayTranslatorFactory implements TranslatorFactory<Object, Collection<Object>> { @Override public Translator<Object, Collection<Object>> create(TypeKey<Object> tk, final CreateContext ctx, final Path path) { final Class<?> arrayType = tk.getTypeAsClass(); if (!arrayType.isArray()) return null; final Type componentType = GenericTypeReflector.getArrayComponentType(arrayType); final Translator<Object, Object> componentTranslator = ctx.getTranslator(new TypeKey<>(componentType, tk), ctx, path); return new Translator<Object, Collection<Object>>() { @Override public Object load(Collection<Object> node, LoadContext ctx, Path path) throws SkipException { if (node == null) throw new SkipException(); List<Object> list = new ArrayList<>(node.size()); for (Object componentNode: node) { try { Object value = componentTranslator.load(componentNode, ctx, path); list.add(value); } catch (SkipException ex) { // No prob skip that one } } // We can't use List.toArray() because it doesn't work with primitives final Object array = Array.newInstance(GenericTypeReflector.erase(componentType), list.size()); for (int i=0; i<list.size(); i++) { Array.set(array, i, list.get(i)); } return array; } @Override public Collection<Object> save(Object pojo, boolean index, SaveContext ctx, Path path) throws SkipException { // Use same behavior as collections. if (pojo == null) throw new SkipException(); int len = Array.getLength(pojo); // If it's empty, might as well skip it - the datastore doesn't store empty lists if (len == 0) throw new SkipException(); List<Object> list = new ArrayList<>(len); for (int i=0; i<len; i++) { try { Object value = Array.get(pojo, i); Object addNode = componentTranslator.save(value, index, ctx, path); list.add(addNode); } catch (SkipException ex) { // No problem, skip that element } } return list; } }; } }