mirror of https://github.com/kurisufriend/Clover
parent
2d291bf9f0
commit
b0271587d7
@ -0,0 +1,36 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings; |
||||||
|
|
||||||
|
public interface SettingProvider { |
||||||
|
int getInt(String key, int def); |
||||||
|
|
||||||
|
void putInt(String key, int value); |
||||||
|
|
||||||
|
long getLong(String key, long def); |
||||||
|
|
||||||
|
void putLong(String key, long value); |
||||||
|
|
||||||
|
boolean getBoolean(String key, boolean def); |
||||||
|
|
||||||
|
void putBoolean(String key, boolean value); |
||||||
|
|
||||||
|
String getString(String key, String def); |
||||||
|
|
||||||
|
void putString(String key, String value); |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings; |
||||||
|
|
||||||
|
import android.content.SharedPreferences; |
||||||
|
|
||||||
|
public class SharedPreferencesSettingProvider implements SettingProvider { |
||||||
|
private SharedPreferences prefs; |
||||||
|
|
||||||
|
public SharedPreferencesSettingProvider(SharedPreferences prefs) { |
||||||
|
this.prefs = prefs; |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getInt(String key, int def) { |
||||||
|
return prefs.getInt(key, def); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putInt(String key, int value) { |
||||||
|
prefs.edit().putInt(key, value).apply(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getLong(String key, long def) { |
||||||
|
return prefs.getLong(key, def); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putLong(String key, long value) { |
||||||
|
prefs.edit().putLong(key, value).apply(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean getBoolean(String key, boolean def) { |
||||||
|
return prefs.getBoolean(key, def); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putBoolean(String key, boolean value) { |
||||||
|
prefs.edit().putBoolean(key, value).apply(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getString(String key, String def) { |
||||||
|
return prefs.getString(key, def); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putString(String key, String value) { |
||||||
|
prefs.edit().putString(key, value).apply(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName; |
||||||
|
|
||||||
|
public class BooleanJsonSetting extends JsonSetting { |
||||||
|
@SerializedName("value") |
||||||
|
public boolean value; |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName; |
||||||
|
|
||||||
|
public class IntegerJsonSetting extends JsonSetting { |
||||||
|
@SerializedName("value") |
||||||
|
public int value; |
||||||
|
} |
@ -0,0 +1,29 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
public class JsonSettings { |
||||||
|
@SerializedName("settings") |
||||||
|
List<JsonSetting> settings = new ArrayList<>(); |
||||||
|
} |
@ -0,0 +1,152 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import org.floens.chan.core.settings.SettingProvider; |
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
|
||||||
|
public class JsonSettingsProvider implements SettingProvider { |
||||||
|
public final JsonSettings jsonSettings; |
||||||
|
private Callback callback; |
||||||
|
|
||||||
|
private Map<String, JsonSetting> byKey = new HashMap<>(); |
||||||
|
|
||||||
|
public JsonSettingsProvider(JsonSettings jsonSettings, Callback callback) { |
||||||
|
this.jsonSettings = jsonSettings; |
||||||
|
this.callback = callback; |
||||||
|
|
||||||
|
load(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public int getInt(String key, int def) { |
||||||
|
JsonSetting setting = byKey.get(key); |
||||||
|
if (setting != null) { |
||||||
|
return ((IntegerJsonSetting) setting).value; |
||||||
|
} else { |
||||||
|
return def; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putInt(String key, int value) { |
||||||
|
JsonSetting jsonSetting = byKey.get(key); |
||||||
|
if (jsonSetting == null) { |
||||||
|
IntegerJsonSetting v = new IntegerJsonSetting(); |
||||||
|
v.value = value; |
||||||
|
byKey.put(key, v); |
||||||
|
} else { |
||||||
|
((IntegerJsonSetting) jsonSetting).value = value; |
||||||
|
} |
||||||
|
save(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public long getLong(String key, long def) { |
||||||
|
JsonSetting setting = byKey.get(key); |
||||||
|
if (setting != null) { |
||||||
|
return ((LongJsonSetting) setting).value; |
||||||
|
} else { |
||||||
|
return def; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putLong(String key, long value) { |
||||||
|
JsonSetting jsonSetting = byKey.get(key); |
||||||
|
if (jsonSetting == null) { |
||||||
|
LongJsonSetting v = new LongJsonSetting(); |
||||||
|
v.value = value; |
||||||
|
byKey.put(key, v); |
||||||
|
} else { |
||||||
|
((LongJsonSetting) jsonSetting).value = value; |
||||||
|
} |
||||||
|
save(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean getBoolean(String key, boolean def) { |
||||||
|
JsonSetting setting = byKey.get(key); |
||||||
|
if (setting != null) { |
||||||
|
return ((BooleanJsonSetting) setting).value; |
||||||
|
} else { |
||||||
|
return def; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putBoolean(String key, boolean value) { |
||||||
|
JsonSetting jsonSetting = byKey.get(key); |
||||||
|
if (jsonSetting == null) { |
||||||
|
BooleanJsonSetting v = new BooleanJsonSetting(); |
||||||
|
v.value = value; |
||||||
|
byKey.put(key, v); |
||||||
|
} else { |
||||||
|
((BooleanJsonSetting) jsonSetting).value = value; |
||||||
|
} |
||||||
|
save(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getString(String key, String def) { |
||||||
|
JsonSetting setting = byKey.get(key); |
||||||
|
if (setting != null) { |
||||||
|
return ((StringJsonSetting) setting).value; |
||||||
|
} else { |
||||||
|
return def; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void putString(String key, String value) { |
||||||
|
JsonSetting jsonSetting = byKey.get(key); |
||||||
|
if (jsonSetting == null) { |
||||||
|
StringJsonSetting v = new StringJsonSetting(); |
||||||
|
v.value = value; |
||||||
|
byKey.put(key, v); |
||||||
|
} else { |
||||||
|
((StringJsonSetting) jsonSetting).value = value; |
||||||
|
} |
||||||
|
save(); |
||||||
|
} |
||||||
|
|
||||||
|
private void load() { |
||||||
|
byKey.clear(); |
||||||
|
for (JsonSetting setting : jsonSettings.settings) { |
||||||
|
byKey.put(setting.key, setting); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void save() { |
||||||
|
List<JsonSetting> settings = new ArrayList<>(); |
||||||
|
for (Map.Entry<String, JsonSetting> e : byKey.entrySet()) { |
||||||
|
settings.add(e.getValue()); |
||||||
|
} |
||||||
|
jsonSettings.settings = settings; |
||||||
|
callback.save(); |
||||||
|
} |
||||||
|
|
||||||
|
public interface Callback { |
||||||
|
void save(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName; |
||||||
|
|
||||||
|
public class LongJsonSetting extends JsonSetting { |
||||||
|
@SerializedName("value") |
||||||
|
public long value; |
||||||
|
} |
@ -0,0 +1,240 @@ |
|||||||
|
/* |
||||||
|
* Copyright (C) 2011 Google Inc. |
||||||
|
* |
||||||
|
* 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 org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import com.google.gson.Gson; |
||||||
|
import com.google.gson.JsonElement; |
||||||
|
import com.google.gson.JsonObject; |
||||||
|
import com.google.gson.JsonParseException; |
||||||
|
import com.google.gson.JsonPrimitive; |
||||||
|
import com.google.gson.TypeAdapter; |
||||||
|
import com.google.gson.TypeAdapterFactory; |
||||||
|
import com.google.gson.internal.Streams; |
||||||
|
import com.google.gson.reflect.TypeToken; |
||||||
|
import com.google.gson.stream.JsonReader; |
||||||
|
import com.google.gson.stream.JsonWriter; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.util.LinkedHashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
/** |
||||||
|
* Adapts values whose runtime type may differ from their declaration type. This |
||||||
|
* is necessary when a field's type is not the same type that GSON should create |
||||||
|
* when deserializing that field. For example, consider these types: |
||||||
|
* <pre> {@code |
||||||
|
* abstract class Shape { |
||||||
|
* int x; |
||||||
|
* int y; |
||||||
|
* } |
||||||
|
* class Circle extends Shape { |
||||||
|
* int radius; |
||||||
|
* } |
||||||
|
* class Rectangle extends Shape { |
||||||
|
* int width; |
||||||
|
* int height; |
||||||
|
* } |
||||||
|
* class Diamond extends Shape { |
||||||
|
* int width; |
||||||
|
* int height; |
||||||
|
* } |
||||||
|
* class Drawing { |
||||||
|
* Shape bottomShape; |
||||||
|
* Shape topShape; |
||||||
|
* } |
||||||
|
* }</pre> |
||||||
|
* <p>Without additional type information, the serialized JSON is ambiguous. Is |
||||||
|
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code |
||||||
|
* { |
||||||
|
* "bottomShape": { |
||||||
|
* "width": 10, |
||||||
|
* "height": 5, |
||||||
|
* "x": 0, |
||||||
|
* "y": 0 |
||||||
|
* }, |
||||||
|
* "topShape": { |
||||||
|
* "radius": 2, |
||||||
|
* "x": 4, |
||||||
|
* "y": 1 |
||||||
|
* } |
||||||
|
* }}</pre> |
||||||
|
* This class addresses this problem by adding type information to the |
||||||
|
* serialized JSON and honoring that type information when the JSON is |
||||||
|
* deserialized: <pre> {@code |
||||||
|
* { |
||||||
|
* "bottomShape": { |
||||||
|
* "type": "Diamond", |
||||||
|
* "width": 10, |
||||||
|
* "height": 5, |
||||||
|
* "x": 0, |
||||||
|
* "y": 0 |
||||||
|
* }, |
||||||
|
* "topShape": { |
||||||
|
* "type": "Circle", |
||||||
|
* "radius": 2, |
||||||
|
* "x": 4, |
||||||
|
* "y": 1 |
||||||
|
* } |
||||||
|
* }}</pre> |
||||||
|
* Both the type field name ({@code "type"}) and the type labels ({@code |
||||||
|
* "Rectangle"}) are configurable. |
||||||
|
* |
||||||
|
* <h3>Registering Types</h3> |
||||||
|
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field |
||||||
|
* name to the {@link #of} factory method. If you don't supply an explicit type |
||||||
|
* field name, {@code "type"} will be used. <pre> {@code |
||||||
|
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory |
||||||
|
* = RuntimeTypeAdapterFactory.of(Shape.class, "type"); |
||||||
|
* }</pre> |
||||||
|
* Next register all of your subtypes. Every subtype must be explicitly |
||||||
|
* registered. This protects your application from injection attacks. If you |
||||||
|
* don't supply an explicit type label, the type's simple name will be used. |
||||||
|
* <pre> {@code |
||||||
|
* shapeAdapter.registerSubtype(Rectangle.class, "Rectangle"); |
||||||
|
* shapeAdapter.registerSubtype(Circle.class, "Circle"); |
||||||
|
* shapeAdapter.registerSubtype(Diamond.class, "Diamond"); |
||||||
|
* }</pre> |
||||||
|
* Finally, register the type adapter factory in your application's GSON builder: |
||||||
|
* <pre> {@code |
||||||
|
* Gson gson = new GsonBuilder() |
||||||
|
* .registerTypeAdapterFactory(shapeAdapterFactory) |
||||||
|
* .create(); |
||||||
|
* }</pre> |
||||||
|
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code |
||||||
|
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class) |
||||||
|
* .registerSubtype(Rectangle.class) |
||||||
|
* .registerSubtype(Circle.class) |
||||||
|
* .registerSubtype(Diamond.class); |
||||||
|
* }</pre> |
||||||
|
*/ |
||||||
|
public class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory { |
||||||
|
private final Class<?> baseType; |
||||||
|
private final String typeFieldName; |
||||||
|
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>(); |
||||||
|
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>(); |
||||||
|
|
||||||
|
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName) { |
||||||
|
if (typeFieldName == null || baseType == null) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
this.baseType = baseType; |
||||||
|
this.typeFieldName = typeFieldName; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new runtime type adapter using for {@code baseType} using {@code |
||||||
|
* typeFieldName} as the type field name. Type field names are case sensitive. |
||||||
|
*/ |
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) { |
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as |
||||||
|
* the type field name. |
||||||
|
*/ |
||||||
|
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) { |
||||||
|
return new RuntimeTypeAdapterFactory<T>(baseType, "type"); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Registers {@code type} identified by {@code label}. Labels are case |
||||||
|
* sensitive. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if either {@code type} or {@code label} |
||||||
|
* have already been registered on this type adapter. |
||||||
|
*/ |
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) { |
||||||
|
if (type == null || label == null) { |
||||||
|
throw new NullPointerException(); |
||||||
|
} |
||||||
|
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) { |
||||||
|
throw new IllegalArgumentException("types and labels must be unique"); |
||||||
|
} |
||||||
|
labelToSubtype.put(label, type); |
||||||
|
subtypeToLabel.put(type, label); |
||||||
|
return this; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Registers {@code type} identified by its {@link Class#getSimpleName simple |
||||||
|
* name}. Labels are case sensitive. |
||||||
|
* |
||||||
|
* @throws IllegalArgumentException if either {@code type} or its simple name |
||||||
|
* have already been registered on this type adapter. |
||||||
|
*/ |
||||||
|
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) { |
||||||
|
return registerSubtype(type, type.getSimpleName()); |
||||||
|
} |
||||||
|
|
||||||
|
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) { |
||||||
|
if (type.getRawType() != baseType) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
|
||||||
|
final Map<String, TypeAdapter<?>> labelToDelegate |
||||||
|
= new LinkedHashMap<String, TypeAdapter<?>>(); |
||||||
|
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate |
||||||
|
= new LinkedHashMap<Class<?>, TypeAdapter<?>>(); |
||||||
|
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) { |
||||||
|
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue())); |
||||||
|
labelToDelegate.put(entry.getKey(), delegate); |
||||||
|
subtypeToDelegate.put(entry.getValue(), delegate); |
||||||
|
} |
||||||
|
|
||||||
|
return new TypeAdapter<R>() { |
||||||
|
@Override public R read(JsonReader in) throws IOException { |
||||||
|
JsonElement jsonElement = Streams.parse(in); |
||||||
|
JsonElement labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName); |
||||||
|
if (labelJsonElement == null) { |
||||||
|
throw new JsonParseException("cannot deserialize " + baseType |
||||||
|
+ " because it does not define a field named " + typeFieldName); |
||||||
|
} |
||||||
|
String label = labelJsonElement.getAsString(); |
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label); |
||||||
|
if (delegate == null) { |
||||||
|
throw new JsonParseException("cannot deserialize " + baseType + " subtype named " |
||||||
|
+ label + "; did you forget to register a subtype?"); |
||||||
|
} |
||||||
|
return delegate.fromJsonTree(jsonElement); |
||||||
|
} |
||||||
|
|
||||||
|
@Override public void write(JsonWriter out, R value) throws IOException { |
||||||
|
Class<?> srcType = value.getClass(); |
||||||
|
String label = subtypeToLabel.get(srcType); |
||||||
|
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||||
|
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType); |
||||||
|
if (delegate == null) { |
||||||
|
throw new JsonParseException("cannot serialize " + srcType.getName() |
||||||
|
+ "; did you forget to register a subtype?"); |
||||||
|
} |
||||||
|
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject(); |
||||||
|
if (jsonObject.has(typeFieldName)) { |
||||||
|
throw new JsonParseException("cannot serialize " + srcType.getName() |
||||||
|
+ " because it already defines a field named " + typeFieldName); |
||||||
|
} |
||||||
|
JsonObject clone = new JsonObject(); |
||||||
|
clone.add(typeFieldName, new JsonPrimitive(label)); |
||||||
|
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) { |
||||||
|
clone.add(e.getKey(), e.getValue()); |
||||||
|
} |
||||||
|
Streams.write(clone, out); |
||||||
|
} |
||||||
|
}.nullSafe(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
/* |
||||||
|
* Clover - 4chan browser https://github.com/Floens/Clover/
|
||||||
|
* Copyright (C) 2014 Floens |
||||||
|
* |
||||||
|
* This program is free software: you can redistribute it and/or modify |
||||||
|
* it under the terms of the GNU General Public License as published by |
||||||
|
* the Free Software Foundation, either version 3 of the License, or |
||||||
|
* (at your option) any later version. |
||||||
|
* |
||||||
|
* This program is distributed in the hope that it will be useful, |
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
* GNU General Public License for more details. |
||||||
|
* |
||||||
|
* You should have received a copy of the GNU General Public License |
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
package org.floens.chan.core.settings.json; |
||||||
|
|
||||||
|
import com.google.gson.annotations.SerializedName; |
||||||
|
|
||||||
|
public class StringJsonSetting extends JsonSetting { |
||||||
|
@SerializedName("value") |
||||||
|
public String value; |
||||||
|
} |
Loading…
Reference in new issue