/*
* Copyright 2014 OpenMarket Ltd
* Copyright 2017 Vector Creations Ltd
*
* 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.matrix.androidsdk.util;
import com.google.gson.FieldNamingPolicy;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import org.matrix.androidsdk.data.RoomState;
import org.matrix.androidsdk.rest.json.ConditionDeserializer;
import org.matrix.androidsdk.rest.model.AudioMessage;
import org.matrix.androidsdk.rest.model.ContentResponse;
import org.matrix.androidsdk.rest.model.EncryptedEventContent;
import org.matrix.androidsdk.rest.model.Event;
import org.matrix.androidsdk.rest.model.EventContent;
import org.matrix.androidsdk.rest.model.FileMessage;
import org.matrix.androidsdk.rest.model.ImageMessage;
import org.matrix.androidsdk.rest.model.LocationMessage;
import org.matrix.androidsdk.rest.model.MatrixError;
import org.matrix.androidsdk.rest.model.Message;
import org.matrix.androidsdk.rest.model.NewDeviceContent;
import org.matrix.androidsdk.rest.model.PowerLevels;
import org.matrix.androidsdk.rest.model.RoomKeyContent;
import org.matrix.androidsdk.rest.model.RoomMember;
import org.matrix.androidsdk.rest.model.RoomTags;
import org.matrix.androidsdk.rest.model.RoomThirdPartyInvite;
import org.matrix.androidsdk.rest.model.User;
import org.matrix.androidsdk.rest.model.VideoMessage;
import org.matrix.androidsdk.rest.model.bingrules.Condition;
import org.matrix.androidsdk.rest.model.login.RegistrationFlowResponse;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeSet;
/**
* Static methods for converting json into objects.
*/
public class JsonUtils {
private static final String LOG_TAG = "JsonUtils";
private static Gson gson = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC)
.registerTypeAdapter(Condition.class, new ConditionDeserializer())
.create();
// add a call to serializeNulls().
// by default the null parameters are not sent in the requests.
// serializeNulls forces to add them.
private static Gson gsonWithNullSerialization = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC)
.serializeNulls()
.registerTypeAdapter(Condition.class, new ConditionDeserializer())
.create();
// for crypto (canonicalize)
// avoid converting "=" to \u003d
private static Gson gsonWithoutHtmlEscaping = new GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.disableHtmlEscaping()
.excludeFieldsWithModifiers(Modifier.PRIVATE, Modifier.STATIC)
.registerTypeAdapter(Condition.class, new ConditionDeserializer())
.create();
public static Gson getGson(boolean withNullSerialization) {
return withNullSerialization ? gsonWithNullSerialization : gson;
}
public static RoomState toRoomState(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, RoomState.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toRoomState failed " + e.getMessage());
}
return new RoomState();
}
public static User toUser(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, User.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toUser failed " + e.getMessage());
}
return new User();
}
public static RoomMember toRoomMember(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, RoomMember.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toRoomMember failed " + e.getMessage());
}
return new RoomMember();
}
public static RoomTags toRoomTags(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, RoomTags.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toRoomTags failed " + e.getMessage());
}
return new RoomTags();
}
public static MatrixError toMatrixError(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, MatrixError.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toMatrixError failed " + e.getMessage());
}
return new MatrixError();
}
public static JsonElement toJson(RoomMember roomMember) {
try {
return gson.toJsonTree(roomMember);
} catch (Exception e) {
Log.e(LOG_TAG, "## toJson failed " + e.getMessage());
}
return null;
}
public static String getMessageMsgType(JsonElement jsonObject) {
try {
Message message = gson.fromJson(jsonObject, Message.class);
return message.msgtype;
} catch (Exception e) {
Log.e(LOG_TAG, "## getMessageMsgType failed " + e.getMessage());
}
return null;
}
public static Message toMessage(JsonElement jsonObject) {
try {
Message message = gson.fromJson(jsonObject, Message.class);
// Try to return the right subclass
if (Message.MSGTYPE_IMAGE.equals(message.msgtype)) {
return toImageMessage(jsonObject);
}
if (Message.MSGTYPE_VIDEO.equals(message.msgtype)) {
return toVideoMessage(jsonObject);
}
if (Message.MSGTYPE_LOCATION.equals(message.msgtype)) {
return toLocationMessage(jsonObject);
}
// Try to return the right subclass
if (Message.MSGTYPE_FILE.equals(message.msgtype)) {
return toFileMessage(jsonObject);
}
if (Message.MSGTYPE_AUDIO.equals(message.msgtype)) {
return toAudioMessage(jsonObject);
}
// Fall back to the generic Message type
return message;
} catch (Exception e) {
Log.e(LOG_TAG, "## toMessage failed " + e.getMessage());
}
return new Message();
}
public static JsonObject toJson(Message message) {
try {
return (JsonObject) gson.toJsonTree(message);
} catch (Exception e) {
Log.e(LOG_TAG, "## toJson failed " + e.getMessage());
}
return null;
}
public static Event toEvent(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, Event.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toEvent failed " + e.getMessage());
}
return new Event();
}
public static EncryptedEventContent toEncryptedEventContent(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, EncryptedEventContent.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toEncryptedEventContent failed " + e.getMessage());
}
return new EncryptedEventContent();
}
public static EventContent toEventContent(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, EventContent.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toEventContent failed " + e.getMessage());
}
return new EventContent();
}
public static RoomKeyContent toRoomKeyContent(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, RoomKeyContent.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## RoomKeyContent failed " + e.getMessage());
}
return new RoomKeyContent();
}
public static ImageMessage toImageMessage(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, ImageMessage.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toImageMessage failed " + e.getMessage());
}
return new ImageMessage();
}
public static FileMessage toFileMessage(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, FileMessage.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toFileMessage failed " + e.getMessage());
}
return new FileMessage();
}
public static AudioMessage toAudioMessage(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, AudioMessage.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toAudioMessage failed " + e.getMessage());
}
return new AudioMessage();
}
public static VideoMessage toVideoMessage(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, VideoMessage.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toVideoMessage failed " + e.getMessage());
}
return new VideoMessage();
}
public static LocationMessage toLocationMessage(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, LocationMessage.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toLocationMessage failed " + e.getMessage());
}
return new LocationMessage();
}
public static ContentResponse toContentResponse(String jsonString) {
try {
return gson.fromJson(jsonString, ContentResponse.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toContentResponse failed " + e.getMessage());
}
return new ContentResponse();
}
public static PowerLevels toPowerLevels(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, PowerLevels.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toPowerLevels failed " + e.getMessage());
}
return new PowerLevels();
}
public static RoomThirdPartyInvite toRoomThirdPartyInvite(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, RoomThirdPartyInvite.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toRoomThirdPartyInvite failed " + e.getMessage());
}
return new RoomThirdPartyInvite();
}
public static RegistrationFlowResponse toRegistrationFlowResponse(String jsonString) {
try {
return gson.fromJson(jsonString, RegistrationFlowResponse.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toRegistrationFlowResponse failed " + e.getMessage());
}
return new RegistrationFlowResponse();
}
public static JsonObject toJson(Event event) {
try {
return (JsonObject) gson.toJsonTree(event);
} catch (Exception e) {
Log.e(LOG_TAG, "## toJson failed " + e.getMessage());
}
return new JsonObject();
}
public static NewDeviceContent toNewDeviceContent(JsonElement jsonObject) {
try {
return gson.fromJson(jsonObject, NewDeviceContent.class);
} catch (Exception e) {
Log.e(LOG_TAG, "## toNewDeviceContent failed " + e.getMessage());
}
return new NewDeviceContent();
}
/**
* Create a canonicalized json string for an object
* @param object the object to convert
* @return the canonicalized string
*/
public static String getCanonicalizedJsonString(Object object) {
String canonicalizedJsonString = null;
if (null != object) {
if (object instanceof JsonElement) {
canonicalizedJsonString = gsonWithoutHtmlEscaping.toJson(canonicalize((JsonElement)object));
} else {
canonicalizedJsonString = gsonWithoutHtmlEscaping.toJson(canonicalize(gsonWithoutHtmlEscaping.toJsonTree(object)));
}
if (null != canonicalizedJsonString) {
canonicalizedJsonString = canonicalizedJsonString.replace("\\/", "/");
}
}
return canonicalizedJsonString;
}
/**
* Canonicalize a JsonElement element
* @param src the src
* @return the canonicalize element
*/
public static JsonElement canonicalize(JsonElement src) {
// sanity check
if (null == src) {
return null;
}
if (src instanceof JsonArray) {
// Canonicalize each element of the array
JsonArray srcArray = (JsonArray) src;
JsonArray result = new JsonArray();
for (int i = 0; i < srcArray.size(); i++) {
result.add(canonicalize(srcArray.get(i)));
}
return result;
} else if (src instanceof JsonObject) {
// Sort the attributes by name, and the canonicalize each element of the object
JsonObject srcObject = (JsonObject) src;
JsonObject result = new JsonObject();
TreeSet<String> attributes = new TreeSet<>();
for (Map.Entry<String, JsonElement> entry : srcObject.entrySet()) {
attributes.add(entry.getKey());
}
for (String attribute : attributes) {
result.add(attribute, canonicalize(srcObject.get(attribute)));
}
return result;
} else {
return src;
}
}
/**
* Convert a string from an UTF8 String
* @param s the string to convert
* @return the utf-16 string
*/
public static String convertFromUTF8(String s) {
String out = s;
if (null != out) {
try {
byte[] bytes = out.getBytes();
out = new String(bytes, "UTF-8");
} catch (Exception e) {
Log.e(LOG_TAG, "## convertFromUTF8() failed " + e.getMessage());
}
}
return out;
}
/**
* Convert a string to an UTF8 String
* @param s the string to convert
* @return the utf-8 string
*/
public static String convertToUTF8(String s) {
String out = s;
if (null != out) {
try {
byte[] bytes = out.getBytes("UTF-8");
out = new String(bytes);
} catch (Exception e) {
Log.e(LOG_TAG, "## convertToUTF8() failed " + e.getMessage());
}
}
return out;
}
}