Skip to content

Commit

Permalink
Add parser for Optional types to avoid the null madness
Browse files Browse the repository at this point in the history
  • Loading branch information
lucidd committed Sep 11, 2015
1 parent e17858f commit 3e0fd6c
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 0 deletions.
43 changes: 43 additions & 0 deletions src/main/java/com/suse/saltstack/netapi/parser/JsonParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
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;
Expand All @@ -31,10 +32,12 @@
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.Type;
import java.lang.reflect.ParameterizedType;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* Parser for Saltstack responses.
Expand Down Expand Up @@ -79,6 +82,7 @@ public JsonParser(TypeToken<T> type) {
.registerTypeAdapter(Date.class, new DateAdapter().nullSafe())
.registerTypeAdapter(Stats.class, new StatsDeserializer())
.registerTypeAdapter(Arguments.class, new ArgumentsDeserializer())
.registerTypeAdapterFactory(new OptionalTypeAdapterFactory())
.create();
}

Expand Down Expand Up @@ -126,6 +130,45 @@ public Date read(JsonReader jsonReader) throws IOException {
}
}

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

@SuppressWarnings("unchecked")
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> 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<T>) optionalAdapter(elementAdapter);
} else {
return null;
}
}

private <A> TypeAdapter<Optional<A>> optionalAdapter(TypeAdapter<A> innerAdapter) {
return new TypeAdapter<Optional<A>>() {
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);
}
}

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

}

/**
* Deserializer for the Stats object received from the API.
*/
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/com/suse/saltstack/netapi/parser/JsonParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.gson.JsonParseException;
import com.suse.saltstack.netapi.calls.wheel.Key;
import com.google.gson.reflect.TypeToken;
import com.suse.saltstack.netapi.datatypes.Arguments;
import com.suse.saltstack.netapi.datatypes.Job;
import com.suse.saltstack.netapi.datatypes.ScheduledJob;
Expand All @@ -14,7 +15,9 @@
import com.suse.saltstack.netapi.datatypes.Token;
import java.util.Date;
import java.util.Arrays;
import java.util.Optional;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import org.junit.Test;

Expand All @@ -24,6 +27,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;

/**
* Json parser unit tests.
Expand Down Expand Up @@ -202,6 +206,27 @@ public void testSaltStackJobsWithKwargsParser() throws Exception {
assertEquals("lucid", job.getUser());
}


@Test
public void testOptionalParser() {
InputStream is = this.getClass()
.getResourceAsStream("/optional_parser_test.json");
JsonParser<OptionalTest> parser = new JsonParser<>(new TypeToken<OptionalTest>(){});
OptionalTest result = parser.parse(is);
assertFalse(result.nullString.isPresent());
assertFalse(result.absentString.isPresent());
result.valueString.ifPresent((value) ->
assertEquals("string with value", value)
);
List<Optional<Integer>> expected = new LinkedList<>();
expected.add(Optional.of(1));
expected.add(Optional.of(2));
expected.add(Optional.of(3));
expected.add(Optional.empty());
expected.add(Optional.of(5));
assertEquals(expected, result.maybeInts);
}

@Test
public void testSaltStackJobsWithArgsAsKwargsParser() throws Exception {
InputStream is = this.getClass()
Expand Down
16 changes: 16 additions & 0 deletions src/test/java/com/suse/saltstack/netapi/parser/OptionalTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.suse.saltstack.netapi.parser;

import java.util.Optional;
import java.util.List;

/**
* Helper Type to test the Optional Parser
*/
public class OptionalTest {

public Optional<String> nullString = Optional.empty();
public Optional<String> valueString = Optional.empty();
public Optional<String> absentString = Optional.empty();
public List<Optional<Integer>> maybeInts;

}
5 changes: 5 additions & 0 deletions src/test/resources/optional_parser_test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"nullString": null,
"valueString": "string with value",
"maybeInts": [1, 2, 3, null, 5]
}

0 comments on commit 3e0fd6c

Please sign in to comment.