Skip to content

Instantly share code, notes, and snippets.

@ibaca
Last active June 7, 2018 12:22
Show Gist options
  • Save ibaca/89a2056a862a15c24cb93cdcff908b1f to your computer and use it in GitHub Desktop.
Save ibaca/89a2056a862a15c24cb93cdcff908b1f to your computer and use it in GitHub Desktop.
GWT JsInterop DTOs inheritance
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);
}
}
@nfekete
Copy link

nfekete commented May 22, 2018

That's a nice idea. Thanks for sharing!

@ibaca
Copy link
Author

ibaca commented May 22, 2018

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