Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public static Builder builder() {
* @since 3.0.0
*/
public static TypeAdapter<IntersectionLanes> typeAdapter(Gson gson) {
return new AutoValue_IntersectionLanes.GsonTypeAdapter(gson);
return new IntersectionLanesTypeAdapter(new AutoValue_IntersectionLanes.GsonTypeAdapter(gson));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package com.mapbox.api.directions.v5.models;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.mapbox.api.directions.v5.utils.ParseUtils;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* A TypeAdapter for {@link IntersectionLanes} used to optimize JSON deserialization.
*
* <p>Strings in {@link IntersectionLanes#indications()} and
* {@link IntersectionLanes#validIndication()} can accept a limited set of possible values
* (e.g., "straight," "right," "left," etc.).
* This adapter invokes {@link String#intern()} on these strings to save memory.</p>
*/
class IntersectionLanesTypeAdapter extends TypeAdapter<IntersectionLanes> {

private final TypeAdapter<IntersectionLanes> defaultAdapter;

IntersectionLanesTypeAdapter(TypeAdapter<IntersectionLanes> defaultAdapter) {
this.defaultAdapter = defaultAdapter;
}

@Override
public void write(JsonWriter out, IntersectionLanes value) throws IOException {
defaultAdapter.write(out, value);
}

@Override
public IntersectionLanes read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}

final JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject();

final IntersectionLanes.Builder builder = IntersectionLanes.builder();
Map<String, JsonElement> unrecognized = null;

for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
final String key = entry.getKey();
final JsonElement value = entry.getValue();

if (value.isJsonNull()) {
continue;
}

switch (key) {
case "valid":
builder.valid(value.getAsBoolean());
break;

case "active":
builder.active(value.getAsBoolean());
break;

case "valid_indication":
builder.validIndication(value.getAsString().intern());
break;

case "indications":
builder.indications(ParseUtils.parseAndInternJsonStringArray(value.getAsJsonArray()));
break;

case "payment_methods":
builder.paymentMethods(ParseUtils.parseAndInternJsonStringArray(value.getAsJsonArray()));
break;

default:
if (unrecognized == null) {
unrecognized = new LinkedHashMap<>();
}
unrecognized.put(key, value);
break;
}
}

return builder
.unrecognizedJsonProperties(unrecognized)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ public Point location() {
* @since 3.0.0
*/
public static TypeAdapter<StepManeuver> typeAdapter(Gson gson) {
return new AutoValue_StepManeuver.GsonTypeAdapter(gson);
return new StepManeuverTypeAdapter(new AutoValue_StepManeuver.GsonTypeAdapter(gson));
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.mapbox.api.directions.v5.models;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.TypeAdapter;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* A TypeAdapter for {@link StepManeuver} used to optimize JSON deserialization.
*
* <p>Strings {@link StepManeuver#type()} and {@link StepManeuver#modifier()} can accept
* a limited set of possible values.
* This adapter invokes {@link String#intern()} on these strings to save memory.</p>
*/
class StepManeuverTypeAdapter extends TypeAdapter<StepManeuver> {
private final TypeAdapter<StepManeuver> defaultAdapter;

StepManeuverTypeAdapter(TypeAdapter<StepManeuver> defaultAdapter) {
this.defaultAdapter = defaultAdapter;
}

@Override
public void write(JsonWriter out, StepManeuver value) throws IOException {
defaultAdapter.write(out, value);
}

@Override
public StepManeuver read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}

final JsonObject jsonObject = JsonParser.parseReader(in).getAsJsonObject();

final StepManeuver.Builder builder = StepManeuver.builder();
Map<String, JsonElement> unrecognized = null;

for (Map.Entry<String, JsonElement> entry : jsonObject.entrySet()) {
final String key = entry.getKey();
final JsonElement value = entry.getValue();

if (value.isJsonNull()) {
continue;
}

switch (key) {
case "location":
final JsonArray jsonArray = value.getAsJsonArray();
final double[] locations = new double[jsonArray.size()];
for (int i = 0; i < jsonArray.size(); ++i) {
locations[i] = jsonArray.get(i).getAsDouble();
}
builder.rawLocation(locations);
break;

case "bearing_before":
builder.bearingBefore(value.getAsDouble());
break;

case "bearing_after":
builder.bearingAfter(value.getAsDouble());
break;

case "instruction":
builder.instruction(value.getAsString());
break;

case "type":
builder.type(value.getAsString().intern());
break;

case "modifier":
builder.modifier(value.getAsString().intern());
break;

case "exit":
builder.exit(value.getAsInt());
break;

default:
if (unrecognized == null) {
unrecognized = new LinkedHashMap<>();
}
unrecognized.put(key, value);
break;
}
}

return builder
.unrecognizedJsonProperties(unrecognized)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.google.gson.JsonArray;
import com.mapbox.api.directions.v5.models.Bearing;
import com.mapbox.geojson.Point;
import java.util.ArrayList;
Expand Down Expand Up @@ -213,4 +215,19 @@ public static List<Bearing> parseBearings(@Nullable String original) {
public static List<Boolean> parseToBooleans(@Nullable String original) {
return parseToList(SEMICOLON, original, BOOLEAN_PARSER);
}

/**
* Parses strings from json array and invokes {@link String#intern()} on each item.
*
* @param jsonArray json array with string items
* @return List of interned parsed strings
*/
@NonNull
public static List<String> parseAndInternJsonStringArray(JsonArray jsonArray) {
final List<String> result = new ArrayList<>(jsonArray.size());
for (int i = 0; i < jsonArray.size(); ++i) {
result.add(jsonArray.get(i).getAsString().intern());
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;

import com.mapbox.api.directions.v5.DirectionsCriteria;
import com.mapbox.core.TestUtils;
Expand Down Expand Up @@ -102,4 +103,46 @@ public void testFromJson_etc2Payment() {

assertEquals(intersectionLanes, intersectionLanesFromJson);
}

@Test
public void testIndicationsAreInterned() {
IntersectionLanes intersectionLanes = IntersectionLanes.builder()
.validIndication("straight")
.indications(Arrays.asList("straight","straight"))
.build();

IntersectionLanes deserialized = IntersectionLanes.fromJson(
intersectionLanes.toJson()
);

List<String> indications = deserialized.indications();
assertEquals(Arrays.asList("straight","straight"), indications);
assertEquals("straight", deserialized.validIndication());

assertSame(indications.get(0), indications.get(1));
assertSame(indications.get(0), deserialized.validIndication());
}

@Test
public void testPaymentMethodsAreInterned() {
List<String> paymentMethods = Arrays.asList(
DirectionsCriteria.PAYMENT_METHOD_GENERAL,
DirectionsCriteria.PAYMENT_METHOD_GENERAL
);

IntersectionLanes intersectionLanes = IntersectionLanes.builder()
.paymentMethods(paymentMethods)
.build();

IntersectionLanes deserialized = IntersectionLanes.fromJson(
intersectionLanes.toJson()
);

assertEquals(paymentMethods, deserialized.paymentMethods());

assertSame(
deserialized.paymentMethods().get(0),
deserialized.paymentMethods().get(1)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;

import com.mapbox.core.TestUtils;
import com.mapbox.geojson.Point;
Expand Down Expand Up @@ -71,4 +72,42 @@ public void testFromJson() {

compareJson(stepManeuverJsonString, jsonStr);
}

@Test
public void testTypesAreInterned() {
StepManeuver stepManeuver1 = StepManeuver.builder()
.rawLocation(new double[] {1.0, 2.0})
.type(StepManeuver.TURN)
.build();

StepManeuver stepManeuver2 = StepManeuver.builder()
.rawLocation(new double[] {1.0, 3.0})
.type(StepManeuver.TURN)
.build();

StepManeuver deserialized1 = StepManeuver.fromJson(stepManeuver1.toJson());
StepManeuver deserialized2 = StepManeuver.fromJson(stepManeuver2.toJson());

assertEquals(deserialized1.type(), deserialized2.type());
assertSame(deserialized1.type(), deserialized2.type());
}

@Test
public void testModifierAreInterned() {
StepManeuver stepManeuver1 = StepManeuver.builder()
.rawLocation(new double[] {1.0, 2.0})
.modifier("slight right")
.build();

StepManeuver stepManeuver2 = StepManeuver.builder()
.rawLocation(new double[] {1.0, 3.0})
.modifier("slight right")
.build();

StepManeuver deserialized1 = StepManeuver.fromJson(stepManeuver1.toJson());
StepManeuver deserialized2 = StepManeuver.fromJson(stepManeuver2.toJson());

assertEquals(deserialized1.modifier(), deserialized2.modifier());
assertSame(deserialized1.modifier(), deserialized2.modifier());
}
}