/* * 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 com.github.filosganga.geogson.model.Coordinates; 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.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonToken; import com.google.gson.stream.JsonWriter; /** * The Gson TypeAdapter to serialize/de-serialize all the {@link Positions} instances. */ public class PositionsAdapter extends TypeAdapter<Positions> { @Override public void write(JsonWriter out, Positions value) throws IOException { if (value == null) { out.nullValue(); } else { out.beginArray(); if (value instanceof SinglePosition) { SinglePosition sp = (SinglePosition) value; out.value(sp.coordinates().getLon()).value(sp.coordinates().getLat()); } else { for (Positions child : value.children()) { write(out, child); } } out.endArray(); } } @Override public Positions read(JsonReader in) throws IOException { Positions parsed; JsonToken peek = in.peek(); if (peek == JsonToken.NULL) { in.nextNull(); parsed = null; } else if (peek == JsonToken.BEGIN_ARRAY) { parsed = parsePositions(in); } else { throw new IllegalArgumentException("The json must be an array or null: " + in.peek()); } return parsed; } private Positions parsePositions(JsonReader in) throws IOException { Optional<Positions> parsed = Optional.absent(); if (in.peek() != JsonToken.BEGIN_ARRAY) { throw new IllegalArgumentException("The given json is not a valid positions"); } in.beginArray(); if (in.peek() == JsonToken.NUMBER) { parsed = Optional.of(parseSinglePosition(in)); } else if (in.peek() == JsonToken.BEGIN_ARRAY) { while (in.hasNext()) { Positions thisPositions = parsePositions(in); // fix bug #30: according to the recursion (i.e. the array structure; // recognize that we came from a recursion because no parsed has no // value yet): convert the already parsed Positions to the // LinearPositions/AreaPositions matching the recursion level if (parsed.equals(Optional.absent()) && thisPositions instanceof LinearPositions) { AreaPositions areaPositions = new AreaPositions(ImmutableList.of((LinearPositions) thisPositions)); parsed = Optional.of((Positions) areaPositions); } else if (parsed.equals(Optional.absent()) && thisPositions instanceof AreaPositions) { MultiDimensionalPositions multiPositions = new MultiDimensionalPositions(ImmutableList.of((AreaPositions) thisPositions)); parsed = Optional.of((Positions) multiPositions); } else { // mergeFn() does all the rest, if parsed has a value parsed = parsed.transform(mergeFn(thisPositions)).or(Optional.of(thisPositions)); } } } in.endArray(); return parsed.orNull(); } private Function<Positions, Positions> mergeFn(final Positions p) { return new Function<Positions, Positions>() { @Override public Positions apply(Positions input) { return input.merge(p); } }; } private Positions parseSinglePosition(JsonReader in) throws IOException { Positions parsed; double lon = in.nextDouble(); double lat = in.nextDouble(); // Skip eventual altitude or other dimensions while (in.peek() != JsonToken.END_ARRAY) { in.skipValue(); } parsed = new SinglePosition(Coordinates.of(lon, lat)); return parsed; } }