package com.googlecode.totallylazy.json; import com.googlecode.totallylazy.Option; import com.googlecode.totallylazy.Sequence; import com.googlecode.totallylazy.reflection.Fields; import com.googlecode.totallylazy.reflection.Reflection; import com.googlecode.totallylazy.reflection.Types; import com.googlecode.totallylazy.time.Dates; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.math.BigDecimal; import java.util.AbstractMap; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import static com.googlecode.totallylazy.Classes.allClasses; import static com.googlecode.totallylazy.LazyException.lazyException; import static com.googlecode.totallylazy.Pair.pair; import static com.googlecode.totallylazy.Sequences.reject; import static com.googlecode.totallylazy.Sequences.sequence; import static com.googlecode.totallylazy.Sets.set; import static com.googlecode.totallylazy.Sets.union; import static com.googlecode.totallylazy.Unchecked.cast; import static com.googlecode.totallylazy.predicates.Predicates.and; import static com.googlecode.totallylazy.predicates.Predicates.is; import static com.googlecode.totallylazy.predicates.Predicates.where; import static com.googlecode.totallylazy.reflection.Fields.modifiers; import static com.googlecode.totallylazy.reflection.Reflection.synthetic; import static com.googlecode.totallylazy.reflection.Types.matches; import static java.util.Collections.unmodifiableSet; public abstract class JsonRecord extends AbstractMap<String, Object> { private final Map<String, Object> _otherFields = new HashMap<>(); public static <T extends JsonRecord> T parse(Class<T> recordType, String data) { return create(recordType, Json.map(data)); } public static <T extends JsonRecord> T create(Class<T> recordType, Map<String, Object> data) { try { T instance = Reflection.newInstance(recordType); for (Entry<String, Object> entry : data.entrySet()) { instance.put(entry.getKey(), entry.getValue()); } return instance; } catch (Exception e) { throw lazyException(e); } } @Override public Set<Entry<String, Object>> entrySet() { return unmodifiableSet(reject(union(fieldSet(), _otherFields.entrySet()), entry -> entry.getValue() == null).toSet()); } private Set<Entry<String, Object>> fieldSet() { Sequence<Field> fields = fields(); return set(fields. reject(where(modifiers, synthetic)). map(f -> pair(f.getName(), Fields.get(f, this)))); } private Sequence<Field> fields() { Sequence<Class<?>> classes = allClasses(getClass()). reject(Class::isInterface). takeWhile(c -> !c.equals(JsonRecord.class)); return classes.flatMap(Fields.fields()); } @Override public Object get(Object key) { return field(key.toString()). map(Fields.value(this)). getOrElse(() -> _otherFields.get(key)); } private Option<Field> field(String name) { return fields(). find(f -> f.getName().equalsIgnoreCase(name)). map(Fields::access); } @Override public Object put(String key, Object value) { try { Option<Field> field = field(key); if (field.isDefined()) { Field actual = field.get(); actual.set(this, Coercer.coerce(actual.getGenericType(), value)); return Fields.get(actual, this); } else { return _otherFields.put(key, value); } } catch (IllegalAccessException e) { throw lazyException(e); } } @Override public String toString() { return Json.json(this); } }