com.datastax.dse.driver.internal.core.graph.GraphSON2SerdeTP Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.dse.driver.internal.core.graph;
import com.datastax.dse.driver.api.core.data.geometry.Geometry;
import com.datastax.dse.driver.api.core.data.geometry.LineString;
import com.datastax.dse.driver.api.core.data.geometry.Point;
import com.datastax.dse.driver.api.core.data.geometry.Polygon;
import com.datastax.dse.driver.api.core.graph.predicates.Geo;
import com.datastax.dse.driver.api.core.graph.predicates.Search;
import com.datastax.dse.driver.internal.core.data.geometry.DefaultLineString;
import com.datastax.dse.driver.internal.core.data.geometry.DefaultPoint;
import com.datastax.dse.driver.internal.core.data.geometry.DefaultPolygon;
import com.datastax.dse.driver.internal.core.data.geometry.Distance;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.util.AndP;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
import org.apache.tinkerpop.gremlin.structure.io.graphson.AbstractObjectDeserializer;
import org.apache.tinkerpop.gremlin.structure.io.graphson.GraphSONTokens;
import org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule;
import org.apache.tinkerpop.shaded.jackson.core.JsonGenerator;
import org.apache.tinkerpop.shaded.jackson.core.JsonParser;
import org.apache.tinkerpop.shaded.jackson.databind.DeserializationContext;
import org.apache.tinkerpop.shaded.jackson.databind.SerializerProvider;
import org.apache.tinkerpop.shaded.jackson.databind.deser.std.StdDeserializer;
import org.apache.tinkerpop.shaded.jackson.databind.jsontype.TypeSerializer;
import org.apache.tinkerpop.shaded.jackson.databind.module.SimpleModule;
import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdScalarSerializer;
import org.apache.tinkerpop.shaded.jackson.databind.ser.std.StdSerializer;
public class GraphSON2SerdeTP {
/**
* A Jackson Module to use for TinkerPop serialization/deserialization. It extends {@link
* org.apache.tinkerpop.gremlin.structure.io.graphson.TinkerPopJacksonModule} because of the
* specific typing format used in GraphSON.
*/
public static class DseGraphModule extends TinkerPopJacksonModule {
private static final long serialVersionUID = 1L;
public DseGraphModule() {
super("dse-driver-2.0");
addSerializer(DefaultPoint.class, new PointGeometrySerializer());
addSerializer(DefaultLineString.class, new LineStringGeometrySerializer());
addSerializer(DefaultPolygon.class, new PolygonGeometrySerializer());
addSerializer(Distance.class, new DistanceGeometrySerializer());
// override TinkerPop's P predicates because of DSE's Search and Geo predicates
addSerializer(P.class, new DsePJacksonSerializer());
addSerializer(EditDistance.class, new EditDistanceSerializer());
addDeserializer(DefaultLineString.class, new LineStringGeometryDeserializer());
addDeserializer(DefaultPoint.class, new PointGeometryDeserializer());
addDeserializer(DefaultPolygon.class, new PolygonGeometryDeserializer());
addDeserializer(Distance.class, new DistanceGeometryDeserializer());
// override TinkerPop's P predicates because of DSE's Search and Geo predicates
addDeserializer(P.class, new DsePJacksonDeserializer());
}
@SuppressWarnings("rawtypes")
@Override
public Map getTypeDefinitions() {
Map definitions = new HashMap<>();
definitions.put(DefaultLineString.class, "LineString");
definitions.put(DefaultPoint.class, "Point");
definitions.put(DefaultPolygon.class, "Polygon");
definitions.put(byte[].class, "Blob");
definitions.put(Distance.class, "Distance");
definitions.put(P.class, "P");
return definitions;
}
@Override
public String getTypeNamespace() {
return "dse";
}
abstract static class AbstractGeometryJacksonDeserializer
extends StdDeserializer {
private static final long serialVersionUID = 1L;
AbstractGeometryJacksonDeserializer(final Class clazz) {
super(clazz);
}
public abstract T parse(final String val);
@Override
public T deserialize(
final JsonParser jsonParser, final DeserializationContext deserializationContext)
throws IOException {
return parse(jsonParser.getText());
}
}
abstract static class AbstractGeometryJacksonSerializer
extends StdScalarSerializer {
private static final long serialVersionUID = 1L;
AbstractGeometryJacksonSerializer(final Class clazz) {
super(clazz);
}
@Override
public void serialize(
final T value, final JsonGenerator gen, final SerializerProvider serializerProvider)
throws IOException {
gen.writeString(value.asWellKnownText());
}
}
public static class LineStringGeometrySerializer
extends AbstractGeometryJacksonSerializer {
private static final long serialVersionUID = 1L;
LineStringGeometrySerializer() {
super(LineString.class);
}
}
public static class LineStringGeometryDeserializer
extends AbstractGeometryJacksonDeserializer {
private static final long serialVersionUID = 1L;
LineStringGeometryDeserializer() {
super(DefaultLineString.class);
}
@Override
public DefaultLineString parse(final String val) {
return (DefaultLineString) LineString.fromWellKnownText(val);
}
}
public static class PolygonGeometrySerializer
extends AbstractGeometryJacksonSerializer {
private static final long serialVersionUID = 1L;
PolygonGeometrySerializer() {
super(Polygon.class);
}
}
public static class PolygonGeometryDeserializer
extends AbstractGeometryJacksonDeserializer {
private static final long serialVersionUID = 1L;
PolygonGeometryDeserializer() {
super(DefaultPolygon.class);
}
@Override
public DefaultPolygon parse(final String val) {
return (DefaultPolygon) Polygon.fromWellKnownText(val);
}
}
public static class PointGeometrySerializer extends AbstractGeometryJacksonSerializer {
private static final long serialVersionUID = 1L;
PointGeometrySerializer() {
super(Point.class);
}
}
public static class PointGeometryDeserializer
extends AbstractGeometryJacksonDeserializer {
private static final long serialVersionUID = 1L;
PointGeometryDeserializer() {
super(DefaultPoint.class);
}
@Override
public DefaultPoint parse(final String val) {
return (DefaultPoint) Point.fromWellKnownText(val);
}
}
public static class DistanceGeometrySerializer
extends AbstractGeometryJacksonSerializer {
private static final long serialVersionUID = 1L;
DistanceGeometrySerializer() {
super(Distance.class);
}
}
public static class DistanceGeometryDeserializer
extends AbstractGeometryJacksonDeserializer {
private static final long serialVersionUID = 1L;
DistanceGeometryDeserializer() {
super(Distance.class);
}
@Override
public Distance parse(final String val) {
return Distance.fromWellKnownText(val);
}
}
@SuppressWarnings("rawtypes")
static final class DsePJacksonSerializer extends StdScalarSerializer {
private static final long serialVersionUID = 1L;
DsePJacksonSerializer() {
super(P.class);
}
@Override
public void serialize(
final P p, final JsonGenerator jsonGenerator, final SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("predicateType", getPredicateType(p));
jsonGenerator.writeStringField(
GraphSONTokens.PREDICATE,
p instanceof ConnectiveP
? p instanceof AndP ? GraphSONTokens.AND : GraphSONTokens.OR
: p.getBiPredicate().toString());
if (p instanceof ConnectiveP) {
jsonGenerator.writeArrayFieldStart(GraphSONTokens.VALUE);
for (final P predicate : ((ConnectiveP) p).getPredicates()) {
jsonGenerator.writeObject(predicate);
}
jsonGenerator.writeEndArray();
} else {
if (p.getValue() instanceof Collection) {
jsonGenerator.writeArrayFieldStart(GraphSONTokens.VALUE);
for (final Object object : (Collection) p.getValue()) {
jsonGenerator.writeObject(object);
}
jsonGenerator.writeEndArray();
} else {
jsonGenerator.writeObjectField(GraphSONTokens.VALUE, p.getValue());
}
}
jsonGenerator.writeEndObject();
}
private String getPredicateType(P p) {
if (p.getBiPredicate() instanceof SearchPredicate) {
return Search.class.getSimpleName();
} else if (p.getBiPredicate() instanceof GeoPredicate) {
return Geo.class.getSimpleName();
} else {
return P.class.getSimpleName();
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
static final class DsePJacksonDeserializer extends AbstractObjectDeserializer
{
private static final long serialVersionUID = 1L;
DsePJacksonDeserializer() {
super(P.class);
}
@Override
public P createObject(final Map data) {
final String predicate = (String) data.get(GraphSONTokens.PREDICATE);
final String predicateType = (String) data.get("predicateType");
final Object value = data.get(GraphSONTokens.VALUE);
if (predicate.equals(GraphSONTokens.AND) || predicate.equals(GraphSONTokens.OR)) {
return predicate.equals(GraphSONTokens.AND)
? new AndP((List) value)
: new OrP((List
) value);
} else {
try {
if (value instanceof Collection) {
if (predicate.equals("between")) {
return P.between(((List) value).get(0), ((List) value).get(1));
} else if (predicateType.equals(P.class.getSimpleName())
&& predicate.equals("inside")) {
return P.between(((List) value).get(0), ((List) value).get(1));
} else if (predicate.equals("outside")) {
return P.outside(((List) value).get(0), ((List) value).get(1));
} else if (predicate.equals("within")) {
return P.within((Collection) value);
} else if (predicate.equals("without")) {
return P.without((Collection) value);
} else {
return (P)
P.class.getMethod(predicate, Collection.class).invoke(null, (Collection) value);
}
} else {
if (predicate.equals(SearchPredicate.prefix.name())) {
return Search.prefix((String) value);
} else if (predicate.equals(SearchPredicate.tokenPrefix.name())) {
return Search.tokenPrefix((String) value);
} else if (predicate.equals(SearchPredicate.regex.name())) {
return Search.regex((String) value);
} else if (predicate.equals(SearchPredicate.tokenRegex.name())) {
return Search.tokenRegex((String) value);
} else if (predicate.equals(SearchPredicate.token.name())) {
return Search.token((String) value);
} else if (predicate.equals(SearchPredicate.fuzzy.name())) {
Map arguments = (Map) value;
return Search.fuzzy(
(String) arguments.get("query"), (int) arguments.get("distance"));
} else if (predicate.equals(SearchPredicate.tokenFuzzy.name())) {
Map arguments = (Map) value;
return Search.tokenFuzzy(
(String) arguments.get("query"), (int) arguments.get("distance"));
} else if (predicate.equals(SearchPredicate.phrase.name())) {
Map arguments = (Map) value;
return Search.phrase(
(String) arguments.get("query"), (int) arguments.get("distance"));
} else if (predicateType.equals(Geo.class.getSimpleName())
&& predicate.equals(GeoPredicate.inside.name())) {
return Geo.inside(
((Distance) value).getCenter(),
((Distance) value).getRadius(),
Geo.Unit.DEGREES);
} else if (predicateType.equals(Geo.class.getSimpleName())
&& predicate.equals(GeoPredicate.insideCartesian.name())) {
return Geo.inside(((Distance) value).getCenter(), ((Distance) value).getRadius());
} else {
return (P) P.class.getMethod(predicate, Object.class).invoke(null, value);
}
}
} catch (final Exception e) {
throw new IllegalStateException(e.getMessage(), e);
}
}
}
}
public static class EditDistanceSerializer extends StdSerializer {
private static final long serialVersionUID = 1L;
EditDistanceSerializer() {
super(EditDistance.class);
}
@Override
public void serialize(
EditDistance editDistance, JsonGenerator generator, SerializerProvider provider)
throws IOException {
generator.writeObject(
ImmutableMap.of("query", editDistance.query, "distance", editDistance.distance));
}
@Override
public void serializeWithType(
EditDistance editDistance,
JsonGenerator generator,
SerializerProvider provider,
TypeSerializer serializer)
throws IOException {
serialize(editDistance, generator, provider);
}
}
}
public static class DriverObjectsModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public DriverObjectsModule() {
super("datastax-driver-module");
addSerializer(ObjectGraphNode.class, new ObjectGraphNodeGraphSON2Serializer());
}
static final class ObjectGraphNodeGraphSON2Serializer extends StdSerializer {
private static final long serialVersionUID = 1L;
protected ObjectGraphNodeGraphSON2Serializer() {
super(ObjectGraphNode.class);
}
@Override
public void serialize(
ObjectGraphNode objectGraphNode,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider)
throws IOException {
jsonGenerator.writeObject(objectGraphNode.as(Object.class));
}
@Override
public void serializeWithType(
ObjectGraphNode objectGraphNode,
JsonGenerator jsonGenerator,
SerializerProvider serializerProvider,
TypeSerializer typeSerializer)
throws IOException {
serialize(objectGraphNode, jsonGenerator, serializerProvider);
}
}
}
}