/*
* This file is part of LanternServer, licensed under the MIT License (MIT).
*
* Copyright (c) LanternPowered <https://www.lanternpowered.org>
* Copyright (c) SpongePowered <https://www.spongepowered.org>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the Software), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package org.lanternpowered.server.data.translator;
import com.google.common.collect.Lists;
import com.google.common.reflect.TypeToken;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
import org.lanternpowered.server.data.persistence.AbstractDataTranslator;
import org.spongepowered.api.data.DataContainer;
import org.spongepowered.api.data.DataQuery;
import org.spongepowered.api.data.DataSerializable;
import org.spongepowered.api.data.DataView;
import org.spongepowered.api.data.MemoryDataContainer;
import org.spongepowered.api.data.persistence.InvalidDataException;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
public class JsonTranslator extends AbstractDataTranslator<JsonObject> {
private static final JsonTranslator INSTANCE = new JsonTranslator();
/**
* Get the instance of this translator.
*
* @return The instance of this translator
*/
public static JsonTranslator instance() {
return INSTANCE;
}
private static final Pattern DOUBLE = Pattern.compile("^[-+]?[0-9]*\\.?[0-9]+[dD]$");
private static final Pattern DOUBLE_UNTYPED = Pattern.compile("^[-+]?[0-9]*\\.?[0-9]+$");
private static final Pattern FLOAT = Pattern.compile("^[-+]?[0-9]*\\.?[0-9]+[fF]$");
private static final Pattern LONG = Pattern.compile("^[-+]?[0-9]+[lL]$");
private static final Pattern BYTE = Pattern.compile("^[-+]?[0-9]+[bB]$");
private static final Pattern SHORT = Pattern.compile("^[-+]?[0-9]+[sS]$");
private static final Pattern INTEGER = Pattern.compile("^[-+]?[0-9]+$");
private JsonTranslator() {
super("lantern", "json", TypeToken.of(JsonObject.class));
}
@Override
public JsonObject translate(DataView view) throws InvalidDataException {
return toJson(view.getValues(false)).getAsJsonObject();
}
@Override
public DataContainer translate(JsonObject obj) throws InvalidDataException {
return (DataContainer) fromJson(null, obj);
}
private Object fromJson(@Nullable DataView container, JsonElement json) {
if (json.isJsonObject()) {
if (container == null) {
container = new MemoryDataContainer(DataView.SafetyMode.NO_DATA_CLONED);
}
for (Entry<String, JsonElement> en : json.getAsJsonObject().entrySet()) {
final String key = en.getKey();
final JsonElement element = en.getValue();
if (element.isJsonObject()) {
fromJson(container.createView(DataQuery.of(key)), element);
} else {
container.set(DataQuery.of(key), fromJson(null, json));
}
}
return container;
} else if (json.isJsonArray()) {
final JsonArray array = json.getAsJsonArray();
final List<Object> objects = Lists.newArrayListWithCapacity(array.size());
int ints = 0;
int bytes = 0;
for (int i = 0; i < array.size(); i++) {
final Object object = fromJson(null, array.get(i));
objects.add(object);
if (object instanceof Integer) {
ints++;
}
if (object instanceof Byte) {
bytes++;
}
}
if (bytes == objects.size()) {
final Byte[] array0 = new Byte[bytes];
for (int i = 0; i < bytes; i++) {
array0[i] = (Byte) objects.get(i);
}
return array0;
} else if (ints == objects.size()) {
final Integer[] array0 = new Integer[ints];
for (int i = 0; i < bytes; i++) {
array0[i] = (Integer) objects.get(i);
}
return array0;
} else {
return objects;
}
} else if (json.isJsonPrimitive()) {
final String value = json.getAsString();
if (DOUBLE.matcher(value).matches()) {
return Double.parseDouble(value.substring(0, value.length() - 1));
} else if (FLOAT.matcher(value).matches()) {
return Float.parseFloat(value.substring(0, value.length() - 1));
} else if (LONG.matcher(value).matches()) {
return Long.parseLong(value.substring(0, value.length() - 1));
} else if (SHORT.matcher(value).matches()) {
return Short.parseShort(value.substring(0, value.length() - 1));
} else if (BYTE.matcher(value).matches()) {
return Byte.parseByte(value.substring(0, value.length() - 1));
} else if (INTEGER.matcher(value).matches()) {
return Integer.parseInt(value);
} else if (DOUBLE_UNTYPED.matcher(value).matches()) {
return Double.parseDouble(value);
} else {
if ("true".equalsIgnoreCase(value)) {
return true;
} else if ("false".equalsIgnoreCase(value)) {
return false;
}
return value;
}
}
return json;
}
@SuppressWarnings("unchecked")
private JsonElement toJson(Object object) {
if (object instanceof DataView || object instanceof DataSerializable || object instanceof Map<?,?>) {
final Map<DataQuery, Object> map;
if (object instanceof DataView) {
map = ((DataView) object).getValues(false);
} else if (object instanceof DataSerializable) {
map = ((DataSerializable) object).toContainer().getValues(false);
} else {
map = (Map<DataQuery, Object>) object;
}
return toJson(new JsonObject(), map);
} else if (object instanceof List<?>) {
final JsonArray array = new JsonArray();
final List<?> object0 = (List<?>) object;
for (Object anObject0 : object0) {
array.add(toJson(anObject0));
}
return array;
} else if (object instanceof Byte) {
return new JsonPrimitive(object.toString() + "b");
} else if (object instanceof Short) {
return new JsonPrimitive(object.toString() + "s");
} else if (object instanceof Integer) {
return new JsonPrimitive(object.toString());
} else if (object instanceof Long) {
return new JsonPrimitive(object.toString() + "l");
} else if (object instanceof Double) {
return new JsonPrimitive(object.toString() + "d");
} else if (object instanceof Float) {
return new JsonPrimitive(object.toString() + "f");
} else if (object instanceof Integer[]) {
final Integer[] object0 = (Integer[]) object;
final JsonArray array = new JsonArray();
for (Integer anObject0 : object0) {
array.add(new JsonPrimitive(anObject0.toString()));
}
return array;
} else if (object instanceof Byte[]) {
final Byte[] object0 = (Byte[]) object;
final JsonArray array = new JsonArray();
for (Byte anObject0 : object0) {
array.add(new JsonPrimitive(anObject0.toString() + "b"));
}
return array;
} else {
return new JsonPrimitive(object.toString());
}
}
private JsonObject toJson(JsonObject json, Map<DataQuery, Object> map) {
for (Entry<DataQuery, Object> en : map.entrySet()) {
json.add(en.getKey().asString('.'), toJson(en.getValue()));
}
return json;
}
}