Java tutorial
/* * Copyright 2013 Filippo De Luca - me@filippodeluca.com * * Licensed 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.github.filosganga.geogson.gson; import java.io.IOException; import java.util.ArrayList; import com.github.filosganga.geogson.model.*; import com.github.filosganga.geogson.model.positions.AreaPositions; import com.github.filosganga.geogson.model.positions.LinearPositions; import com.github.filosganga.geogson.model.positions.MultiDimensionalPositions; import com.github.filosganga.geogson.model.positions.Positions; import com.github.filosganga.geogson.model.positions.SinglePosition; import com.github.filosganga.geogson.util.ChainableOptional; import com.google.common.base.Optional; import com.google.common.base.Supplier; import com.google.gson.Gson; 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; import com.google.gson.stream.JsonWriter; /** * The Gson TypeAdapterFactory responsible to serialize/de-serialize all the {@link Geometry}, {@link Feature} * and {@link FeatureCollection} instances. */ public class GeometryAdapterFactory implements TypeAdapterFactory { @Override @SuppressWarnings("unchecked") public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) { if (Geometry.class.isAssignableFrom(type.getRawType())) { return (TypeAdapter<T>) new GeometryAdapter(gson); } else if (Positions.class.isAssignableFrom(type.getRawType())) { return (TypeAdapter<T>) new PositionsAdapter(); } else if (Feature.class.isAssignableFrom(type.getRawType())) { return (TypeAdapter<T>) new FeatureAdapter(gson); } else if (FeatureCollection.class.isAssignableFrom(type.getRawType())) { return (TypeAdapter<T>) new FeatureCollectionAdapter(gson); } else { return null; } } private static class GeometryAdapter extends TypeAdapter<Geometry> { private final Gson gson; private GeometryAdapter(Gson gson) { this.gson = gson; } @Override public void write(JsonWriter out, Geometry value) throws IOException { if (value == null) { out.nullValue(); } else { out.beginObject(); out.name("type").value(value.type().getValue()); if (value.type() == Geometry.Type.GEOMETRY_COLLECTION) { out.name("geometries"); //$NON-NLS-1$ out.beginArray(); GeometryCollection geometries = (GeometryCollection) value; for (Geometry<?> geometry : geometries.getGeometries()) { this.gson.getAdapter(Geometry.class).write(out, geometry); } out.endArray(); } else { out.name("coordinates"); gson.getAdapter(Positions.class).write(out, value.positions()); } out.endObject(); } } @Override public Geometry<?> read(JsonReader in) throws IOException { Geometry<?> geometry = null; if (in.peek() == JsonToken.NULL) { in.nextNull(); } else if (in.peek() == JsonToken.BEGIN_OBJECT) { in.beginObject(); String type = null; Positions positions = null; Geometry<?> geometries = null; while (in.hasNext()) { String name = in.nextName(); if ("type".equals(name)) { type = in.nextString(); } else if ("coordinates".equals(name)) { positions = readPosition(in); } else if ("geometries".equals(name)) { geometries = readGeometries(in); } else { in.skipValue(); } } geometry = buildGeometry(type, positions, geometries); in.endObject(); } else { throw new IllegalArgumentException("The given json is not a valid Geometry: " + in.peek()); } return geometry; } private Positions readPosition(JsonReader in) throws IOException { return this.gson.getAdapter(Positions.class).read(in); } private Geometry<?> readGeometries(JsonReader in) throws IOException { Geometry<?> parsed; JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); parsed = null; } else if (peek == JsonToken.BEGIN_ARRAY) { parsed = parseGeometries(in); } else { throw new IllegalArgumentException("The json must be an array or null: " + in.peek()); } return parsed; } private Geometry<?> parseGeometries(JsonReader in) throws IOException { Optional<Geometry<?>> parsed = Optional.absent(); if (in.peek() != JsonToken.BEGIN_ARRAY) { throw new IllegalArgumentException("The given json is not a valid GeometryCollection"); } in.beginArray(); if (in.peek() == JsonToken.BEGIN_OBJECT) { ArrayList<Geometry<?>> geometries = new ArrayList<Geometry<?>>(); while (in.hasNext()) { @SuppressWarnings("rawtypes") Geometry geometry = this.gson.getAdapter(Geometry.class).read(in); geometries.add(geometry); } parsed = Optional.<Geometry<?>>of(GeometryCollection.of(geometries)); } in.endArray(); return parsed.orNull(); } private Geometry<?> buildGeometry(final String type, Positions positions, Geometry<?> geometries) { // Take care, the order is important! return ChainableOptional.of(buildGeometryCollection(type, geometries)) .or(buildMultiPolygon(type, positions)).or(buildPolygon(type, positions)) .or(buildLinearRing(type, positions)).or(buildMultiLineString(type, positions)) .or(buildLineString(type, positions)).or(buildMultiPoint(type, positions)) .or(buildPoint(type, positions)).orFinally(throwUnsupportedType(type)); } private Supplier<Optional<? extends Geometry<?>>> buildPoint(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (type.equalsIgnoreCase(Geometry.Type.POINT.getValue())) { mayGeometry = Optional .<Geometry<?>>of(Point.from(((SinglePosition) coordinates).coordinates())); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildMultiPoint(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (type.equalsIgnoreCase(Geometry.Type.MULTI_POINT.getValue())) { mayGeometry = Optional.<Geometry<?>>of(new MultiPoint((LinearPositions) coordinates)); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildMultiLineString(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (type.equalsIgnoreCase(Geometry.Type.MULTI_LINE_STRING.getValue())) { mayGeometry = Optional.<Geometry<?>>of(new MultiLineString((AreaPositions) coordinates)); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildLineString(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (type.equalsIgnoreCase(Geometry.Type.LINE_STRING.getValue())) { mayGeometry = Optional.<Geometry<?>>of(new LineString((LinearPositions) coordinates)); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildLinearRing(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (type.equalsIgnoreCase(Geometry.Type.LINEAR_RING.getValue())) { LinearPositions linearPositions = (LinearPositions) coordinates; if (linearPositions.isClosed()) { mayGeometry = Optional.<Geometry<?>>of(new LinearRing(linearPositions)); } } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildPolygon(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (Geometry.Type.POLYGON.getValue().equalsIgnoreCase(type)) { mayGeometry = Optional.<Geometry<?>>of(new Polygon((AreaPositions) coordinates)); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildMultiPolygon(final String type, final Positions coordinates) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (Geometry.Type.MULTI_POLYGON.getValue().equalsIgnoreCase(type)) { mayGeometry = Optional .<Geometry<?>>of(new MultiPolygon((MultiDimensionalPositions) coordinates)); } return mayGeometry; } }; } private Supplier<Optional<? extends Geometry<?>>> buildGeometryCollection(final String type, final Geometry<?> geometries) { return new Supplier<Optional<? extends Geometry<?>>>() { @Override public Optional<Geometry<?>> get() { Optional<Geometry<?>> mayGeometry = Optional.absent(); if (Geometry.Type.GEOMETRY_COLLECTION.getValue().equalsIgnoreCase(type)) { mayGeometry = Optional.<Geometry<?>>of(geometries); } return mayGeometry; } }; } private Supplier<Geometry<?>> throwUnsupportedType(final String type) { return new Supplier<Geometry<?>>() { @Override public Geometry<?> get() { throw new IllegalArgumentException("Cannot build a geometry for type: " + type); } }; } } }