blob: df96af0c66b5c2bc33489f7bab69010ec0181735 [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package io.flutter.plugin.common;
import java.nio.ByteBuffer;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A {@link MethodCodec} using UTF-8 encoded JSON method calls and result envelopes.
*
* <p>This codec is guaranteed to be compatible with the corresponding <a
* href="https://api.flutter.dev/flutter/services/JSONMethodCodec-class.html">JSONMethodCodec</a> on
* the Dart side. These parts of the Flutter SDK are evolved synchronously.
*
* <p>Values supported as methods arguments and result payloads are those supported by {@link
* JSONMessageCodec}.
*/
public final class JSONMethodCodec implements MethodCodec {
// This codec must match the Dart codec of the same name in package flutter/services.
public static final JSONMethodCodec INSTANCE = new JSONMethodCodec();
private JSONMethodCodec() {}
@Override
public ByteBuffer encodeMethodCall(MethodCall methodCall) {
try {
final JSONObject map = new JSONObject();
map.put("method", methodCall.method);
map.put("args", JSONUtil.wrap(methodCall.arguments));
return JSONMessageCodec.INSTANCE.encodeMessage(map);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON", e);
}
}
@Override
public MethodCall decodeMethodCall(ByteBuffer message) {
try {
final Object json = JSONMessageCodec.INSTANCE.decodeMessage(message);
if (json instanceof JSONObject) {
final JSONObject map = (JSONObject) json;
final Object method = map.get("method");
final Object arguments = unwrapNull(map.opt("args"));
if (method instanceof String) {
return new MethodCall((String) method, arguments);
}
}
throw new IllegalArgumentException("Invalid method call: " + json);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON", e);
}
}
@Override
public ByteBuffer encodeSuccessEnvelope(Object result) {
return JSONMessageCodec.INSTANCE.encodeMessage(new JSONArray().put(JSONUtil.wrap(result)));
}
@Override
public ByteBuffer encodeErrorEnvelope(
String errorCode, String errorMessage, Object errorDetails) {
return JSONMessageCodec.INSTANCE.encodeMessage(
new JSONArray()
.put(errorCode)
.put(JSONUtil.wrap(errorMessage))
.put(JSONUtil.wrap(errorDetails)));
}
@Override
public ByteBuffer encodeErrorEnvelopeWithStacktrace(
String errorCode, String errorMessage, Object errorDetails, String errorStacktrace) {
return JSONMessageCodec.INSTANCE.encodeMessage(
new JSONArray()
.put(errorCode)
.put(JSONUtil.wrap(errorMessage))
.put(JSONUtil.wrap(errorDetails))
.put(JSONUtil.wrap(errorStacktrace)));
}
@Override
public Object decodeEnvelope(ByteBuffer envelope) {
try {
final Object json = JSONMessageCodec.INSTANCE.decodeMessage(envelope);
if (json instanceof JSONArray) {
final JSONArray array = (JSONArray) json;
if (array.length() == 1) {
return unwrapNull(array.opt(0));
}
if (array.length() == 3) {
final Object code = array.get(0);
final Object message = unwrapNull(array.opt(1));
final Object details = unwrapNull(array.opt(2));
if (code instanceof String && (message == null || message instanceof String)) {
throw new FlutterException((String) code, (String) message, details);
}
}
}
throw new IllegalArgumentException("Invalid envelope: " + json);
} catch (JSONException e) {
throw new IllegalArgumentException("Invalid JSON", e);
}
}
Object unwrapNull(Object value) {
return (value == JSONObject.NULL) ? null : value;
}
}