package com.suse.salt.netapi.parser;

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Optional;

/**
 * TypeAdaptorFactory creating TypeAdapters for Optional
 */
public class OptionalTypeAdapterFactory implements TypeAdapterFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <A> TypeAdapter<A> create(Gson gson, TypeToken<A> typeToken) {
        Type type = typeToken.getType();
        boolean isOptional = typeToken.getRawType() == Optional.class;
        boolean isParameterized = type instanceof ParameterizedType;
        if (isOptional && isParameterized) {
            Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
            TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
            return (TypeAdapter<A>) optionalAdapter(elementAdapter);
        } else {
            return null;
        }
    }

    private <A> TypeAdapter<Optional<A>> optionalAdapter(TypeAdapter<A> innerAdapter) {
        return new TypeAdapter<Optional<A>>() {
            @Override
            public Optional<A> read(JsonReader in) throws IOException {
                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return Optional.empty();
                } else {
                    A value = innerAdapter.read(in);
                    return Optional.of(value);
                }
            }

            @Override
            public void write(JsonWriter out, Optional<A> optional) throws IOException {
                innerAdapter.write(out, optional.orElse(null));
            }
        };
    }
}

@@ -56,24 +56,104 @@
 .gson.Gson;%0A
+import com.google.gson.JsonElement;%0Aimport com.google.gson.JsonSyntaxException;%0A
 import com.g
@@ -219,16 +219,67 @@
 actory;%0A
+import com.google.gson.internal.bind.TypeAdapters;%0A
 import c
@@ -1770,87 +1770,997 @@
     
-A value = innerAdapter.read(in);%0A                    return Optional.of(value);
+JsonElement json = TypeAdapters.JSON_ELEMENT.read(in);%0A                    try %7B%0A                        A value = innerAdapter.fromJsonTree(json);%0A                        return Optional.of(value);%0A                    %7D%0A                    catch (JsonSyntaxException e) %7B%0A                        /**%0A                         * Note : This is a workaround and it only exists because salt doesn't differentiate between a%0A                         * non-existent grain and a grain which exists but has value set to empty String.%0A                         *%0A                         * If an object is expected but instead empty string comes in then we return empty Optional.%0A                         */%0A                        if (json.isJsonPrimitive() && json.getAsJsonPrimitive().isString() &&%0A                                json.getAsString().isEmpty()) %7B%0A                            return Optional.empty();%0A                        %7D%0A                        throw e;%0A                    %7D
 %0A   

Change empty strings to optional.empty