package de.jpaw.bonaparte.core; import java.math.BigDecimal; import java.math.BigInteger; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.UUID; import org.joda.time.Instant; import org.joda.time.LocalDate; import org.joda.time.LocalDateTime; import org.joda.time.LocalTime; import de.jpaw.bonaparte.enums.BonaNonTokenizableEnum; import de.jpaw.bonaparte.enums.BonaTokenizableEnum; import de.jpaw.bonaparte.pojos.meta.AlphanumericElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.BasicNumericElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.BinaryElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.EnumDataItem; import de.jpaw.bonaparte.pojos.meta.FieldDefinition; import de.jpaw.bonaparte.pojos.meta.MiscElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.NumericElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.ObjectReference; import de.jpaw.bonaparte.pojos.meta.TemporalElementaryDataItem; import de.jpaw.bonaparte.pojos.meta.XEnumDataItem; import de.jpaw.enums.XEnum; import de.jpaw.util.ByteArray; /** Represents composer which does not serialize, but instead puts all objects into a Map. * Only the clear() and add() methods of the Map interface are used by this implementation. * This implementation could be used to convert the object into a structure suitable for FreeMarker or to JSON. * For converted objects, the PQON can be stored optionally. */ public class MapComposer extends NoOpComposer<RuntimeException> implements MessageComposer<RuntimeException> { final protected Map<String, Object> storage; final protected boolean storeNulls; final protected boolean storePQON; // set to true for JSON conversions static interface Store { void store(FieldDefinition di, Object x); void storeKey(Object key); // only used for Map } protected Store currentStorage = null; static public Map<String,Object> marshal(BonaCustom obj) { MapComposer mc = new MapComposer(); obj.serializeSub(mc); return mc.getStorage(); } static public Map<String,Object> toJsonMap(BonaCustom obj) { return marshal(obj, false, true); } static public Map<String,Object> marshal(BonaCustom obj, boolean storeNulls, boolean storePQON) { final Map<String, Object> map = new HashMap<String, Object>(); if (storePQON) map.put(MimeTypes.JSON_FIELD_PQON, obj.ret$PQON()); MapComposer mc = new MapComposer(map, storeNulls, storePQON); obj.serializeSub(mc); return mc.getStorage(); } /** Creates a new ListComposer for a given preallocated external storage. * keepObjects = true replaces the prior ListObjComposer */ public MapComposer(final Map<String, Object> storage, boolean storeNulls, boolean storePQON) { this.storage = storage; this.storeNulls = storeNulls; this.storePQON = storePQON; } /** Creates a new ListComposer for a given preallocated external storage. * keepObjects = true replaces the prior ListObjComposer */ public MapComposer(final Map<String, Object> storage) { this(storage, true, false); } /** Creates a new ListComposer, creating an own internal storage. */ public MapComposer() { this(new HashMap<String, Object>(), true, false); } public Map<String, Object> getStorage() { return storage; } public void reset() { storage.clear(); } protected void store(FieldDefinition di, Object x) { if (currentStorage != null) { currentStorage.store(di, x); } else { if (x != null || storeNulls) storage.put(di.getName(), x); } } @Override public void writeNull(FieldDefinition di) { store(di, null); } @Override public void writeNullCollection(FieldDefinition di) { store(di, null); } @Override public void addField(AlphanumericElementaryDataItem di, String s) { if (di == StaticMeta.MAP_INDEX_META_STRING) { // just remember the field name for later... currentStorage.storeKey(s); return; } store(di, s); } @Override public void addField(MiscElementaryDataItem di, boolean b) { store(di, Boolean.valueOf(b)); } @Override public void addField(MiscElementaryDataItem di, char c) { store(di, Character.valueOf(c)); } @Override public void addField(BasicNumericElementaryDataItem di, double d) { store(di, Double.valueOf(d)); } @Override public void addField(BasicNumericElementaryDataItem di, float f) { store(di, Float.valueOf(f)); } @Override public void addField(BasicNumericElementaryDataItem di, byte n) { store(di, Byte.valueOf(n)); } @Override public void addField(BasicNumericElementaryDataItem di, short n) { store(di, Short.valueOf(n)); } @Override public void addField(BasicNumericElementaryDataItem di, int n) { if (di == StaticMeta.MAP_INDEX_META_INTEGER) { // just remember the field name for later... currentStorage.storeKey(n); return; } store(di, Integer.valueOf(n)); } @Override public void addField(BasicNumericElementaryDataItem di, long n) { if (di == StaticMeta.MAP_INDEX_META_LONG) { // just remember the field name for later... currentStorage.storeKey(n); return; } store(di, Long.valueOf(n)); } @Override public void addField(BasicNumericElementaryDataItem di, BigInteger n) { store(di, n); } @Override public void addField(NumericElementaryDataItem di, BigDecimal n) { store(di, n); } @Override public void addField(MiscElementaryDataItem di, UUID n) { store(di, n); } @Override public void addField(BinaryElementaryDataItem di, ByteArray b) { store(di, b); } @Override public void addField(BinaryElementaryDataItem di, byte[] b) { store(di, b); } @Override public void addField(TemporalElementaryDataItem di, LocalDate t) { store(di, t); } @Override public void addField(TemporalElementaryDataItem di, LocalDateTime t) { store(di, t); } @Override public void addField(TemporalElementaryDataItem di, LocalTime t) { store(di, t); } @Override public void addField(TemporalElementaryDataItem di, Instant t) { store(di, t); } @Override public void addField(ObjectReference di, BonaCustom obj) { if (obj == null) { writeNull(di); } else { store(di, marshal(obj, storeNulls, storePQON)); } } @Override public void addEnum(EnumDataItem di, BasicNumericElementaryDataItem ord, BonaNonTokenizableEnum n) { if (n == null) writeNull(di); else store(di, Integer.valueOf(n.ordinal())); } @Override public void addEnum(EnumDataItem di, AlphanumericElementaryDataItem token, BonaTokenizableEnum n) { if (n == null) writeNull(di); else store(di, n.getToken()); } @Override public void addEnum(XEnumDataItem di, AlphanumericElementaryDataItem token, XEnum<?> n) { if (n == null) writeNull(di); else store(di, n.getToken()); } @Override public boolean addExternal(ObjectReference di, Object obj) { return false; } @Override public void addField(ObjectReference di, Map<String, Object> obj) { store(di, obj); } @Override public void addField(ObjectReference di, List<Object> obj) { store(di, obj); } @Override public void addField(ObjectReference di, Object obj) { store(di, obj); } static protected class ListStorage implements Store { private final List<Object> currentList; protected ListStorage(List<Object> currentList) { this.currentList = currentList; } @Override public void store(FieldDefinition di, Object x) { currentList.add(x); } @Override public void storeKey(Object key) { throw new RuntimeException("Logic error: storeKey called for List/Set/Array subtype"); } } static protected class MapStorage implements Store { private final Map<Object,Object> currentMap; private Object lastKey = null; protected MapStorage(Map<Object,Object> currentMap) { this.currentMap = currentMap; } @Override public void store(FieldDefinition di, Object x) { currentMap.put(lastKey, x); } @Override public void storeKey(Object key) { lastKey = key; } } @Override public void startArray(FieldDefinition di, int currentMembers, int sizeOfElement) { List<Object> currentList = new ArrayList<Object>(currentMembers); store(di, currentList); currentStorage = new ListStorage(currentList); } @Override public void startMap(FieldDefinition di, int currentMembers) { Map<Object,Object> currentMap = new HashMap<Object,Object>(currentMembers); store(di, currentMap); currentStorage = new MapStorage(currentMap); } @Override public void terminateMap() { currentStorage = null; } @Override public void terminateArray() { currentStorage = null; } }