package spimedb; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import jcog.tree.rtree.point.DoubleND; import spimedb.util.JSON; import java.io.IOException; import java.io.Serializable; import java.util.function.BiConsumer; /** * Created by me on 1/18/17. */ @JsonSerialize(using = NObject.NObjectSerializer.class) public interface NObject extends Serializable { //public final static String POINT = "."; String id(); String name(); String[] tags(); void forEach(BiConsumer<String, Object> each); <X> X get(String tag); default <X> X getOr(String key, X ifMissing) { Object val = get(key); if (val == null) return ifMissing; return (X)val; } DoubleND min(); DoubleND max(); default String toJSONString() { return toJSONString(false); } default String toJSONString(boolean pretty) { return new String(JSON.toJSONBytes(this, pretty)); } default boolean bounded() { DoubleND min = min(); return (min != null && min.dim() > 0 && !min.isInfNeg() && !max().isInfPos()); } default String description() { Object d = get(NObject.DESC); return d == null ? "" : d.toString(); } String ID = "I"; String NAME = "N"; String TAG = ">"; String CONTENT = "<"; String BOUND = "@"; String DESC = "_"; String LINESTRING = "g-"; String POLYGON = "g*"; /** content-type, like MIME */ String TYPE = "T"; /** intensional inheritance */ String INH = "inh"; String TAG_PUBLIC = "public"; default boolean has(String key) { return get(key)!=null; } default boolean equalsDeep(NObject n) { //TODO more efficient comparison return toString().equals(n.toString()); //return toString().equals(n.toString()); } static MutableNObject fromJSON(String json) { JsonNode x = JSON.fromJSON(json, JsonNode.class); JsonNode idNode = x.get(ID); if (idNode == null) throw new RuntimeException("invalid nobject JSON: " + json); MutableNObject y = new MutableNObject(idNode.textValue().toString()); x.fields().forEachRemaining(e -> { String k = e.getKey(); switch (k) { case ID: return; //ID already processed } JsonNode v = e.getValue(); Object w = v; if (v.isTextual()) w = v.textValue(); else if (v.isNumber()) w = v.numberValue(); //TODO else ... y.put(k, w); }); return y; } class NObjectSerializer extends JsonSerializer<NObject> { @Override public void serialize(NObject o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeStartObject(); { jsonGenerator.writeStringField(ID, o.id()); o.forEach((fieldName, pojo) -> { if (pojo == null) return; try { jsonGenerator.writeObjectField(fieldName, pojo); } catch (IOException e) { //e.printStackTrace(); } }); writeBounds(o, jsonGenerator); } jsonGenerator.writeEndObject(); } protected void writeBounds(NObject o, JsonGenerator jsonGenerator) throws IOException { //zip the min/max bounds if (o.bounded()) { DoubleND min = o.min(); DoubleND max = o.max(); boolean point = max == null || min.equals(max); if (!point) { jsonGenerator.writeFieldName(BOUND); jsonGenerator.writeStartArray(); if (!max.equals(min)) { int dim = min.dim(); for (int i = 0; i < dim; i++) { double a = min.coord[i]; double b = max.coord[i]; if (a == b) { jsonGenerator.writeNumber(a); } else { if (a == Float.NEGATIVE_INFINITY && b == Float.POSITIVE_INFINITY) { jsonGenerator.writeNumber(Float.NaN); } else { jsonGenerator.writeStartArray(); jsonGenerator.writeNumber(a); jsonGenerator.writeNumber(b); jsonGenerator.writeEndArray(); } } } } else { JSON.writeArrayValues(min.coord, jsonGenerator); } jsonGenerator.writeEndArray(); } } } } }