Last active
June 7, 2018 12:22
-
-
Save ibaca/89a2056a862a15c24cb93cdcff908b1f to your computer and use it in GitHub Desktop.
GWT JsInterop DTOs inheritance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.EXISTING_PROPERTY; | |
import static com.fasterxml.jackson.annotation.JsonTypeInfo.Id.NAME; | |
import static jsinterop.annotations.JsPackage.GLOBAL; | |
import com.fasterxml.jackson.annotation.JsonIgnore; | |
import com.fasterxml.jackson.annotation.JsonSubTypes; | |
import com.fasterxml.jackson.annotation.JsonTypeInfo; | |
import com.fasterxml.jackson.annotation.JsonTypeName; | |
import javax.annotation.Nullable; | |
import jsinterop.annotations.JsOverlay; | |
import jsinterop.annotations.JsType; | |
@JsonTypeInfo(use = NAME, include = EXISTING_PROPERTY, property = "type", visible = true) | |
@JsonSubTypes({ | |
@JsonSubTypes.Type(value = GeoJson.Feature.class, name = "Feature"), | |
@JsonSubTypes.Type(value = GeoJson.FeatureCollection.class, name = "FeatureCollection") | |
}) | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
class GeoJson { | |
public String type; | |
public final @JsOverlay @JsonIgnore Type getTypeEnum() { return Type.valueOf(type); } | |
public final @JsOverlay @JsonIgnore void setTypeEnum(Type type) { this.type = type.name(); } | |
public static @JsOverlay FeatureCollection featureCollection(Feature... features) { | |
FeatureCollection o = new FeatureCollection(); | |
o.setTypeEnum(Type.FeatureCollection); | |
o.features = features; | |
return o; | |
} | |
public static @JsOverlay Feature feature(Geometry geometry) { return feature(null, geometry); } | |
public static @JsOverlay Feature feature(@Nullable String featureId, Geometry geometry) { | |
Feature o = new Feature(); | |
o.setTypeEnum(Type.Feature); | |
o.id = featureId; | |
o.geometry = geometry; | |
return o; | |
} | |
public static @JsOverlay Point point(double x, double y) { return point(new double[] { x, y }); } | |
public static @JsOverlay Point point(double[] coordinates) { | |
Point o = new Point(); | |
o.setTypeEnum(Geometry.Type.Point); | |
o.coordinates = coordinates; | |
return o; | |
} | |
public static @JsOverlay Polygon polygon(double[][] coordinates) { | |
Polygon o = new Polygon(); | |
o.setTypeEnum(Geometry.Type.Polygon); | |
o.coordinates = new double[][][] { coordinates }; | |
return o; | |
} | |
public enum Type {Feature, FeatureCollection} | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
public static final class Feature extends GeoJson { | |
public @Nullable String id; | |
public Geometry geometry; | |
} | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
public static class FeatureCollection extends GeoJson { | |
public Feature[] features; | |
} | |
@JsonTypeInfo(use = NAME, include = EXISTING_PROPERTY, property = "type", visible = true) | |
@JsonSubTypes({ | |
@JsonSubTypes.Type(value = Point.class, name = "Point"), | |
@JsonSubTypes.Type(value = Polygon.class, name = "Polygon") | |
}) | |
@JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
public static abstract class Geometry { | |
public String type; | |
public final @JsOverlay @JsonIgnore Geometry.Type getTypeEnum() { return Geometry.Type.valueOf(type); } | |
public final @JsOverlay @JsonIgnore void setTypeEnum(Geometry.Type type) { this.type = type.name(); } | |
public final @JsOverlay <T> T accept(GeometryVisitor<T> fn) { switch (getTypeEnum()) { | |
case Point: return fn.point((Point) this); | |
case Polygon: return fn.polygon((Polygon) this); | |
default: throw new UnsupportedOperationException("unexpected type " + type); | |
} } | |
public static @JsOverlay @Nullable Point isPoint(@Nullable Geometry g) { | |
return g == null ? null : g.accept(new GeometryVisitor<Point>() { | |
@Override public Point point(Point g) { return g; } | |
@Override public Point polygon(Polygon p) { return null; } | |
}); | |
} | |
public static @JsOverlay @Nullable Polygon isPolygon(@Nullable Geometry g) { | |
return g == null ? null : g.accept(new GeometryVisitor<Polygon>() { | |
@Override public Polygon point(Point g) { return null; } | |
@Override public Polygon polygon(Polygon p) { return p; } | |
}); | |
} | |
public enum Type {Point, Polygon} | |
} | |
@JsonTypeName("Point") @JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
public static class Point extends Geometry { | |
public double[] coordinates; | |
public final @JsOverlay @JsonIgnore double x() { return coordinates[0]; } | |
public final @JsOverlay @JsonIgnore double y() { return coordinates[1]; } | |
} | |
@JsonTypeName("Polygon") @JsType(namespace = GLOBAL, name = "Object", isNative = true) | |
public static final class Polygon extends Geometry { | |
public double[][][] coordinates; | |
public final @JsOverlay @JsonIgnore double[][] shell() { return coordinates[0]; } | |
} | |
public interface GeometryVisitor<T> { | |
T point(Point g); | |
T polygon(Polygon p); | |
} | |
} |
Nice! we have the whole REST API implemented using DTOs with great success, and we only need inheritance in some specific cases, so this, although a bit ugly, works perfectly! and keeps the transport layer super light, performant and simple! 🙌
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
That's a nice idea. Thanks for sharing!