001/*
002 * Copyright (c) 2004-2020, Oracle and/or its affiliates.
003 *
004 * Licensed under the 2-clause BSD license.
005 *
006 * Redistribution and use in source and binary forms, with or without
007 * modification, are permitted provided that the following conditions are met:
008 *
009 * 1. Redistributions of source code must retain the above copyright notice,
010 *    this list of conditions and the following disclaimer.
011 *
012 * 2. Redistributions in binary form must reproduce the above copyright notice,
013 *    this list of conditions and the following disclaimer in the documentation
014 *    and/or other materials provided with the distribution.
015 *
016 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
017 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
018 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
019 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
020 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
021 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
022 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
023 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
024 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
025 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
026 * POSSIBILITY OF SUCH DAMAGE.
027 */
028
029package com.oracle.labs.mlrg.olcut.config.json;
030
031import com.fasterxml.jackson.core.JsonParseException;
032import com.fasterxml.jackson.core.JsonParser;
033import com.fasterxml.jackson.core.ObjectCodec;
034import com.fasterxml.jackson.databind.DeserializationContext;
035import com.fasterxml.jackson.databind.JsonNode;
036import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
037import com.oracle.labs.mlrg.olcut.provenance.io.FlatMarshalledProvenance;
038import com.oracle.labs.mlrg.olcut.provenance.io.ListMarshalledProvenance;
039import com.oracle.labs.mlrg.olcut.provenance.io.MapMarshalledProvenance;
040import com.oracle.labs.mlrg.olcut.provenance.io.MarshalledProvenance;
041import com.oracle.labs.mlrg.olcut.provenance.io.ObjectMarshalledProvenance;
042import com.oracle.labs.mlrg.olcut.provenance.io.SimpleMarshalledProvenance;
043
044import java.io.IOException;
045import java.util.ArrayList;
046import java.util.HashMap;
047import java.util.Iterator;
048import java.util.Map;
049import java.util.Map.Entry;
050
051/**
052 * Deserialization class to convert JSON into {@link MarshalledProvenance}.
053 */
054public class JsonProvenanceDeserializer extends StdDeserializer<MarshalledProvenance> {
055
056    public JsonProvenanceDeserializer(Class<? extends MarshalledProvenance> provClass) {
057        super(provClass);
058    }
059
060    @Override
061    public MarshalledProvenance deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
062        ObjectCodec oc = jsonParser.getCodec();
063
064        JsonNode node = oc.readTree(jsonParser);
065
066        JsonNode classNode = node.get(JsonProvenanceModule.MARSHALLED_CLASS);
067
068        if (classNode != null) {
069            String className = classNode.textValue();
070            if (ListMarshalledProvenance.class.getName().equals(className)) {
071                JsonNode arrayNode = node.get(JsonProvenanceModule.LIST);
072                if (arrayNode != null) {
073                    ArrayList<FlatMarshalledProvenance> list = new ArrayList<>();
074                    for (JsonNode n : arrayNode) {
075                        FlatMarshalledProvenance fmp = (FlatMarshalledProvenance) oc.treeToValue(n, MarshalledProvenance.class);
076                        list.add(fmp);
077                    }
078                    return new ListMarshalledProvenance(list);
079                } else {
080                    throw new JsonParseException(jsonParser, JsonProvenanceModule.LIST + " field not found in ListMarshalledProvenance.");
081                }
082            } else if (MapMarshalledProvenance.class.getName().equals(className)) {
083                JsonNode objectNode = node.get(JsonProvenanceModule.MAP);
084                if (objectNode != null) {
085                    Map<String, FlatMarshalledProvenance> map = new HashMap<>();
086                    for (Iterator<Entry<String, JsonNode>> it = objectNode.fields(); it.hasNext(); ) {
087                        Entry<String, JsonNode> n = it.next();
088                        map.put(n.getKey(), (FlatMarshalledProvenance) oc.treeToValue(n.getValue(), MarshalledProvenance.class));
089                    }
090                    return new MapMarshalledProvenance(map);
091                } else {
092                    throw new JsonParseException(jsonParser, JsonProvenanceModule.MAP + " field not found in MapMarshalledProvenance.");
093                }
094            } else if (ObjectMarshalledProvenance.class.getName().equals(className)) {
095                if (node.has(JsonProvenanceModule.OBJECT_NAME) && node.has(JsonProvenanceModule.OBJECT_CLASS_NAME) &&
096                        node.has(JsonProvenanceModule.PROVENANCE_CLASS) && node.has(JsonProvenanceModule.MAP)) {
097                    String objectName = node.get(JsonProvenanceModule.OBJECT_NAME).textValue();
098                    String objectClassName = node.get(JsonProvenanceModule.OBJECT_CLASS_NAME).textValue();
099                    String provenanceClass = node.get(JsonProvenanceModule.PROVENANCE_CLASS).textValue();
100                    JsonNode objectNode = node.get(JsonProvenanceModule.MAP);
101                    Map<String, FlatMarshalledProvenance> map = new HashMap<>();
102                    for (Iterator<Entry<String, JsonNode>> it = objectNode.fields(); it.hasNext(); ) {
103                        Entry<String, JsonNode> n = it.next();
104                        map.put(n.getKey(), (FlatMarshalledProvenance) oc.treeToValue(n.getValue(), MarshalledProvenance.class));
105                    }
106                    return new ObjectMarshalledProvenance(objectName,map,objectClassName,provenanceClass);
107                } else {
108                    throw new JsonParseException(jsonParser,"ObjectMarshalledProvenance was missing a required field.");
109                }
110            } else if (SimpleMarshalledProvenance.class.getName().equals(className)) {
111                if (node.has(JsonProvenanceModule.KEY) && node.has(JsonProvenanceModule.VALUE) &&
112                        node.has(JsonProvenanceModule.PROVENANCE_CLASS) && node.has(JsonProvenanceModule.ADDITIONAL) &&
113                        node.has(JsonProvenanceModule.IS_REFERENCE)) {
114                    String key = node.get(JsonProvenanceModule.KEY).textValue();
115                    String value = node.get(JsonProvenanceModule.VALUE).textValue();
116                    String provenanceClass = node.get(JsonProvenanceModule.PROVENANCE_CLASS).textValue();
117                    String additional = node.get(JsonProvenanceModule.ADDITIONAL).textValue();
118                    boolean isReference  = node.get(JsonProvenanceModule.IS_REFERENCE).asBoolean();
119                    return new SimpleMarshalledProvenance(key,value,provenanceClass,isReference,additional);
120                } else {
121                    throw new JsonParseException(jsonParser,"SimpleMarshalledProvenance was missing a required field.");
122                }
123            } else {
124                throw new JsonParseException(jsonParser,"Unexpected marshalled provenance class, found " + className);
125            }
126        } else {
127            throw new JsonParseException(jsonParser,"Marshalled provenance json did not contain subclass name.");
128        }
129    }
130}