package com.googlecode.objectify.impl.translate; import com.googlecode.objectify.ObjectifyFactory; import com.googlecode.objectify.annotation.Mapify; import com.googlecode.objectify.impl.Path; import com.googlecode.objectify.mapper.Mapper; import com.googlecode.objectify.util.GenericUtils; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.Map; /** * <p>This takes a datastore collection and converts it to a POJO Map by letting you select out the key value * using a class of your own devising. The values will be written to the collection, not the keys.</p> * * @author Jeff Schnitzer <jeff@infohazard.org> */ public class MapifyTranslatorFactory implements TranslatorFactory<Map<Object, Object>, Collection<Object>> { @Override public Translator<Map<Object, Object>, Collection<Object>> create(TypeKey<Map<Object, Object>> tk, CreateContext ctx, Path path) { Mapify mapify = tk.getAnnotation(Mapify.class); if (mapify == null) return null; @SuppressWarnings("unchecked") final Class<? extends Map<?, ?>> mapType = tk.getTypeAsClass(); if (!Map.class.isAssignableFrom(mapType)) return null; // We might be here processing the component type of the mapify map! final ObjectifyFactory fact = ctx.getFactory(); Type componentType = GenericUtils.getMapValueType(tk.getType()); final Translator<Object, Object> componentTranslator = fact.getTranslators().get(new TypeKey(componentType, tk), ctx, path); @SuppressWarnings("unchecked") final Mapper<Object, Object> mapper = (Mapper<Object, Object>)fact.construct(mapify.value()); return new TranslatorRecycles<Map<Object, Object>, Collection<Object>>() { @Override public Map<Object, Object> loadInto(Collection<Object> node, LoadContext ctx, Path path, Map<Object, Object> map) throws SkipException { if (node == null) throw new SkipException(); if (map == null) //noinspection unchecked map = (Map<Object, Object>)fact.constructMap(mapType); else map.clear(); for (Object child: node) { try { Object translatedChild = componentTranslator.load(child, ctx, path); Object key = mapper.getKey(translatedChild); map.put(key, translatedChild); } catch (SkipException ex) { // No prob, just skip that one } } return map; } @Override public Collection<Object> save(Map<Object, 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(); Collection<Object> list = new ArrayList<>(pojo.size()); for (Object obj: pojo.values()) { try { Object child = componentTranslator.save(obj, index, ctx, path); list.add(child); } catch (SkipException ex) { // Just skip that node, no prob } } return list; } }; } }