001package net.bramp.ffmpeg.gson;
002
003import static com.google.common.base.Preconditions.checkNotNull;
004
005import com.google.common.base.Optional;
006import com.google.gson.TypeAdapter;
007import com.google.gson.stream.JsonReader;
008import com.google.gson.stream.JsonToken;
009import com.google.gson.stream.JsonWriter;
010import java.io.IOException;
011import java.lang.reflect.Field;
012import java.lang.reflect.InvocationTargetException;
013
014/**
015 * Converts a json object which represents a set of booleans. For example:
016 *
017 * <pre>
018 * <code>
019 * public class Set {
020 *   public boolean a = true;
021 *   public boolean b = false;
022 *   public int c = 1;
023 *   public int d = 0;
024 * }
025 * </code>
026 * </pre>
027 *
028 * is turned into:
029 *
030 * <pre>
031 * {
032 *   "a": true,
033 *   "b": false,
034 *   "c": true,
035 *   "d": false
036 * }
037 * </pre>
038 */
039public class NamedBitsetAdapter<T> extends TypeAdapter<T> {
040
041  final Class<T> clazz;
042
043  public NamedBitsetAdapter(Class<T> clazz) {
044    this.clazz = checkNotNull(clazz);
045  }
046
047  protected Optional<Boolean> readBoolean(JsonReader reader) throws IOException {
048    JsonToken next = reader.peek();
049    switch (next) {
050      case BOOLEAN:
051        return Optional.of(reader.nextBoolean());
052      case NUMBER:
053        return Optional.of(reader.nextInt() != 0);
054      default:
055        reader.skipValue();
056        return Optional.absent();
057    }
058  }
059
060  protected void setField(T target, String name, boolean value) throws IllegalAccessException {
061    try {
062      Field f = clazz.getField(name);
063      if ((boolean.class.equals(f.getType()))) {
064        f.setBoolean(target, value);
065      } else if (int.class.equals(f.getType())) {
066        f.setInt(target, value ? 1 : 0);
067      }
068
069    } catch (NoSuchFieldException e) {
070      // Just continue
071    }
072  }
073
074  @Override
075  public T read(JsonReader reader) throws IOException {
076
077    JsonToken next = reader.peek();
078
079    if (next == JsonToken.NULL) {
080      reader.nextNull();
081      return null;
082    }
083
084    try {
085      T obj = clazz.getDeclaredConstructor().newInstance();
086      reader.beginObject();
087
088      next = reader.peek();
089      while (next != JsonToken.END_OBJECT) {
090        String name = reader.nextName();
091        Optional<Boolean> value = readBoolean(reader);
092
093        if (value.isPresent()) {
094          setField(obj, name, value.get());
095        }
096
097        next = reader.peek();
098      }
099
100      reader.endObject();
101      return obj;
102
103    } catch (InstantiationException
104        | IllegalAccessException
105        | NoSuchMethodException
106        | InvocationTargetException e) {
107      throw new IOException("Reflection error", e);
108    }
109  }
110
111  @Override
112  public void write(JsonWriter writer, T value) throws IOException {
113
114    if (value == null) {
115      writer.nullValue();
116      return;
117    }
118
119    assert value.getClass().equals(clazz);
120
121    writer.beginObject();
122    for (Field f : clazz.getFields()) {
123      try {
124        boolean b;
125        if (boolean.class.equals(f.getType())) {
126          b = f.getBoolean(value);
127        } else if (int.class.equals(f.getType())) {
128          b = f.getInt(value) != 0;
129        } else {
130          continue;
131        }
132
133        writer.name(f.getName());
134        writer.value(b);
135
136      } catch (IllegalAccessException e) {
137        throw new IOException("Reflection error", e);
138      }
139    }
140    writer.endObject();
141  }
142}