001package fr.aumgn.dac2.arena.regions;
002
003import java.io.IOException;
004import java.lang.reflect.Constructor;
005import java.lang.reflect.InvocationTargetException;
006import java.util.HashMap;
007import java.util.Map;
008
009import com.google.gson.Gson;
010import com.google.gson.TypeAdapter;
011import com.google.gson.TypeAdapterFactory;
012import com.google.gson.reflect.TypeToken;
013import com.google.gson.stream.JsonReader;
014import com.google.gson.stream.JsonToken;
015import com.google.gson.stream.JsonWriter;
016
017import fr.aumgn.dac2.shape.ArbitraryFlatShape;
018import fr.aumgn.dac2.shape.CuboidShape;
019import fr.aumgn.dac2.shape.CylinderShape;
020import fr.aumgn.dac2.shape.EllipsoidShape;
021import fr.aumgn.dac2.shape.PolygonalShape;
022import fr.aumgn.dac2.shape.Shape;
023import fr.aumgn.dac2.shape.ShapeName;
024
025public class GsonRegionFactory implements TypeAdapterFactory {
026
027    private static final Map<String, Class<? extends Shape>> classes =
028            new HashMap<String, Class<? extends Shape>>();
029
030    public static void registerShape(Class<? extends Shape> shapeClass) {
031        ShapeName annotation = shapeClass.getAnnotation(ShapeName.class);
032        if (annotation == null) {
033            return;
034        }
035
036        classes.put(annotation.value(), shapeClass);
037    }
038
039    static {
040        registerShape(CuboidShape.class);
041        registerShape(CylinderShape.class);
042        registerShape(PolygonalShape.class);
043        registerShape(EllipsoidShape.class);
044        registerShape(ArbitraryFlatShape.class);
045    }
046
047    private static class RegionTypeAdapter extends TypeAdapter<Region> {
048
049        private final Gson gson;
050        private final Constructor<Region> ctor;
051
052        public RegionTypeAdapter(Gson gson, Constructor<Region> ctor) {
053            this.gson = gson;
054            this.ctor = ctor;
055        }
056
057        @Override
058        public Region read(JsonReader reader) throws IOException {
059            reader.beginObject();
060
061            reader.nextName();
062            String type = reader.nextString();
063            Class<? extends Shape> clazz = classes.get(type);
064            if (clazz == null) {
065                while (reader.peek() != JsonToken.END_OBJECT) {
066                    reader.nextName();
067                    reader.skipValue();
068                }
069                reader.endObject();
070
071                return null;
072            }
073
074            reader.nextName();
075            Shape shape = gson.fromJson(reader, clazz);
076            reader.endObject();
077
078            try {
079                return ctor.newInstance(shape);
080            } catch (IllegalArgumentException _) {
081            } catch (InstantiationException _) {
082            } catch (IllegalAccessException _) {
083            } catch (InvocationTargetException _) {
084            }
085
086            return null;
087        }
088
089        @Override
090        public void write(JsonWriter writer, Region region)
091                throws IOException {
092            Shape shape = region.getShape();
093            ShapeName annotation = shape.getClass()
094                    .getAnnotation(ShapeName.class);
095            if (annotation == null) {
096                writer.nullValue();
097                return;
098            }
099            String shapeName = annotation.value();
100
101            writer.beginObject();
102            writer.name("shape");
103            writer.value(shapeName);
104            writer.name("data");
105            gson.toJson(shape, shape.getClass(), writer);
106            writer.endObject();
107        }
108    }
109
110    @SuppressWarnings("unchecked")
111    @Override
112    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
113        if (!Region.class.isAssignableFrom(type.getRawType())) {
114            return null;
115        }
116
117        try {
118            Constructor<Region> ctor = (Constructor<Region>)
119                    type.getRawType().getDeclaredConstructor(Shape.class);
120            ctor.setAccessible(true);
121            return (TypeAdapter<T>) new RegionTypeAdapter(gson, ctor)
122                    .nullSafe();
123        } catch (SecurityException _) {
124        } catch (NoSuchMethodException _) {
125        }
126
127        return null;
128    }
129}