| // 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; |
| } |
| } |