package org.javers.core.json.typeadapter.change; import com.google.gson.JsonDeserializationContext; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonSerializationContext; import org.javers.common.exception.JaversException; import org.javers.common.exception.JaversExceptionCode; import org.javers.core.commit.CommitMetadata; import org.javers.core.diff.Change; import org.javers.core.diff.changetype.*; import org.javers.core.diff.changetype.container.ArrayChange; import org.javers.core.diff.changetype.container.ListChange; import org.javers.core.diff.changetype.container.SetChange; import org.javers.core.diff.changetype.map.MapChange; import org.javers.core.json.JsonTypeAdapterTemplate; import org.javers.core.metamodel.object.GlobalId; import org.javers.core.metamodel.type.JaversProperty; import org.javers.core.metamodel.type.ManagedType; import org.javers.core.metamodel.type.TypeMapper; import java.util.HashMap; import java.util.Map; class ChangeTypeAdapter<T extends Change> extends JsonTypeAdapterTemplate<T> { private static final String CHANGE_TYPE_FIELD = "changeType"; private static final String AFFECTED_CDO_ID_FIELD = "globalId"; private static final String PROPERTY_FIELD = "property"; private static final String COMMIT_METADATA = "commitMetadata"; private final Map<String, Class<? extends Change>> changeTypeMap; protected final TypeMapper typeMapper; public ChangeTypeAdapter(TypeMapper typeMapper) { this.changeTypeMap = new HashMap<>(); this.typeMapper = typeMapper; initEntry(ValueChange.class); initEntry(ReferenceChange.class); initEntry(NewObject.class); initEntry(ObjectRemoved.class); initEntry(MapChange.class); initEntry(ListChange.class); initEntry(ArrayChange.class); initEntry(SetChange.class); } protected CommitMetadata deserializeCommitMetadata(JsonObject jsonObject, JsonDeserializationContext context) { return context.deserialize(jsonObject.get(COMMIT_METADATA), CommitMetadata.class); } @Override public T fromJson(JsonElement json, JsonDeserializationContext context) { JsonObject jsonObject = (JsonObject) json; String changeTypeField = jsonObject.get(CHANGE_TYPE_FIELD).getAsString(); Class<? extends Change> changeType = decode(changeTypeField); return context.deserialize(json, changeType); } @Override public JsonElement toJson(T change, JsonSerializationContext context) { return createJsonObject(change, context); } protected PropertyChangeStub deserializeStub(JsonObject jsonObject, JsonDeserializationContext context) { GlobalId id = deserializeAffectedCdoId(jsonObject, context); String propertyName = jsonObject.get(PROPERTY_FIELD).getAsString(); ManagedType managedType = typeMapper.getJaversManagedType(id); return new PropertyChangeStub(id, managedType.getProperty(propertyName)); } protected GlobalId deserializeAffectedCdoId(JsonObject jsonObject, JsonDeserializationContext context) { return context.deserialize(jsonObject.get(AFFECTED_CDO_ID_FIELD), GlobalId.class); } protected JsonObject createJsonObject(T change, JsonSerializationContext context) { JsonObject jsonObject = new JsonObject(); jsonObject.addProperty(CHANGE_TYPE_FIELD, encode(change.getClass())); jsonObject.add(AFFECTED_CDO_ID_FIELD, context.serialize(change.getAffectedGlobalId())); if (change.getCommitMetadata().isPresent()) { jsonObject.add(COMMIT_METADATA, context.serialize(change.getCommitMetadata().get())); } if (change instanceof PropertyChange) { jsonObject.addProperty(PROPERTY_FIELD, ((PropertyChange) change).getPropertyName()); } return jsonObject; } @Override public Class getValueType() { return Change.class; } protected class PropertyChangeStub{ GlobalId id; JaversProperty property; PropertyChangeStub(GlobalId id, JaversProperty property) { this.id = id; this.property = property; } String getPropertyName(){ return property.getName(); } } private void initEntry(Class<? extends Change> valueChangeClass) { changeTypeMap.put(encode(valueChangeClass), valueChangeClass); } private String encode(Class<? extends Change> valueChangeClass) { return valueChangeClass.getSimpleName(); } private Class<? extends Change> decode(String changeType){ if (!changeTypeMap.containsKey(changeType)) { throw new JaversException(JaversExceptionCode.MALFORMED_CHANGE_TYPE_FIELD, changeType); } return changeTypeMap.get(changeType); } }