package openeye.struct; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.gson.JsonArray; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParseException; import com.google.gson.JsonSerializationContext; import com.google.gson.JsonSerializer; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import openeye.Log; import openeye.protocol.reports.IReport; import openeye.protocol.reports.ReportAnalytics; import openeye.protocol.reports.ReportCrash; import openeye.protocol.reports.ReportFileContents; import openeye.protocol.reports.ReportFileInfo; import openeye.protocol.reports.ReportKnownFiles; import openeye.protocol.reports.ReportPing; import openeye.protocol.responses.ResponseError; import openeye.protocol.responses.ResponseFileContents; import openeye.protocol.responses.ResponseFileInfo; import openeye.protocol.responses.ResponseKnownCrash; import openeye.protocol.responses.ResponseModMsg; import openeye.protocol.responses.ResponsePong; import openeye.protocol.responses.ResponseRemoveFile; import openeye.protocol.responses.ResponseSuspend; import openeye.responses.IExecutableResponse; import openeye.responses.ResponseErrorAction; import openeye.responses.ResponseFileContentsAction; import openeye.responses.ResponseFileInfoAction; import openeye.responses.ResponseKnownCrashAction; import openeye.responses.ResponseModMsgAction; import openeye.responses.ResponsePongAction; import openeye.responses.ResponseRemoveFileAction; import openeye.responses.ResponseSuspendAction; public class TypedCollections { private abstract static class TypedListConverter<T> implements JsonSerializer<Collection<T>>, JsonDeserializer<Collection<T>> { private final BiMap<String, Class<? extends T>> mapping; private TypedListConverter(BiMap<String, Class<? extends T>> mapping) { this.mapping = mapping; } @Override public JsonElement serialize(Collection<T> src, Type typeOfSrc, JsonSerializationContext context) { JsonArray result = new JsonArray(); for (T entry : src) { final Class<? extends Object> entryClass = entry.getClass(); final String type = mapping.inverse().get(entryClass); if (type != null) { JsonObject serializedReport = context.serialize(entry).getAsJsonObject(); serializedReport.addProperty("type", type); result.add(serializedReport); } else Log.warn("Trying to serialize class without mapping: %s", entryClass); } return result; } @Override public Collection<T> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { JsonArray requests = json.getAsJsonArray(); Collection<T> result = createCollection(); for (JsonElement e : requests) { try { JsonObject obj = e.getAsJsonObject(); JsonElement type = obj.get("type"); String typeId = type.getAsString(); Class<? extends T> cls = mapping.get(typeId); if (cls != null) { T request = context.deserialize(obj, cls); result.add(request); } else Log.warn("Invalid request type: %s", typeId); } catch (Throwable t) { Log.warn(t, "Failed to deserialize request %s", e); } } return result; } protected abstract Collection<T> createCollection(); } public static class ReportsList extends ArrayList<IReport> { private static final long serialVersionUID = -6580030458427773185L; } public static class ResponseList extends ArrayList<IExecutableResponse> { private static final long serialVersionUID = 4069373518963113118L; } private static final BiMap<String, Class<? extends IReport>> REPORTS_TYPES = HashBiMap.create(); private static final BiMap<String, Class<? extends IExecutableResponse>> RESPONSE_TYPES = HashBiMap.create(); public static final Object REPORT_LIST_CONVERTER = new TypedListConverter<IReport>(REPORTS_TYPES) { @Override protected Collection<IReport> createCollection() { return new ReportsList(); } }; public static final Object RESPONSE_LIST_CONVERTER = new TypedListConverter<IExecutableResponse>(RESPONSE_TYPES) { @Override protected Collection<IExecutableResponse> createCollection() { return new ResponseList(); } }; static { REPORTS_TYPES.put(ReportAnalytics.TYPE, ReportAnalytics.class); REPORTS_TYPES.put(ReportFileInfo.TYPE, ReportFileInfo.class); REPORTS_TYPES.put(ReportCrash.TYPE, ReportCrash.class); REPORTS_TYPES.put(ReportPing.TYPE, ReportPing.class); REPORTS_TYPES.put(ReportKnownFiles.TYPE, ReportKnownFiles.class); REPORTS_TYPES.put(ReportFileContents.TYPE, ReportFileContents.class); RESPONSE_TYPES.put(ResponseFileInfo.TYPE, ResponseFileInfoAction.class); RESPONSE_TYPES.put(ResponsePong.TYPE, ResponsePongAction.class); RESPONSE_TYPES.put(ResponseFileContents.TYPE, ResponseFileContentsAction.class); RESPONSE_TYPES.put(ResponseRemoveFile.TYPE, ResponseRemoveFileAction.class); RESPONSE_TYPES.put(ResponseModMsg.TYPE, ResponseModMsgAction.class); RESPONSE_TYPES.put(ResponseError.TYPE, ResponseErrorAction.class); RESPONSE_TYPES.put(ResponseKnownCrash.TYPE, ResponseKnownCrashAction.class); RESPONSE_TYPES.put(ResponseSuspend.TYPE, ResponseSuspendAction.class); } }