1 /*
2  * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
3  *
4  * You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
5  * copy, modify, and distribute this software in source code or binary form for use
6  * in connection with the web services and APIs provided by Facebook.
7  *
8  * As with any software that integrates with the Facebook platform, your use of
9  * this software is subject to the Facebook Developer Principles and Policies
10  * [http://developers.facebook.com/policy/]. This copyright notice shall be
11  * included in all copies or substantial portions of the software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 package host.exp.exponent.utils
22 
23 import android.os.Bundle
24 import org.json.JSONArray
25 import org.json.JSONException
26 import org.json.JSONObject
27 
28 /**
29  * Referenced from [com.facebook.internal.BundleJSONConverter]
30  * https://github.com/facebook/facebook-android-sdk/blob/main/facebook-core/src/main/java/com/facebook/internal/BundleJSONConverter.kt
31  *
32  * com.facebook.internal is solely for the use of other packages within the Facebook SDK for
33  * Android. Use of any of the classes in this package is unsupported, and they may be modified or
34  * removed without warning at any time.
35  *
36  * A helper class that can round trip between JSON and Bundle objects that contains the types:
37  * Boolean, Integer, Long, Double, String If other types are found, an IllegalArgumentException is
38  * thrown.
39  */
40 
41 object BundleJSONConverter {
42   private val SETTERS: MutableMap<Class<*>, Setter> = HashMap()
43 
44   @JvmStatic
45   @Throws(JSONException::class)
convertToJSONnull46   fun convertToJSON(bundle: Bundle): JSONObject {
47     val json = JSONObject()
48     for (key in bundle.keySet()) {
49       val value =
50         bundle[key] // Null is not supported.
51           ?: continue
52 
53       // Special case List<String> as getClass would not work, since List is an interface
54       if (value is List<*>) {
55         val jsonArray = JSONArray()
56         val listValue = value as List<String>
57         for (stringValue in listValue) {
58           jsonArray.put(stringValue)
59         }
60         json.put(key, jsonArray)
61         continue
62       }
63 
64       // Special case Bundle as it's one way, on the return it will be JSONObject
65       if (value is Bundle) {
66         json.put(key, convertToJSON(value))
67         continue
68       }
69       val setter =
70         SETTERS[value.javaClass]
71           ?: throw IllegalArgumentException("Unsupported type: " + value.javaClass)
72       setter.setOnJSON(json, key, value)
73     }
74     return json
75   }
76 
77   @JvmStatic
78   @Throws(JSONException::class)
convertToBundlenull79   fun convertToBundle(jsonObject: JSONObject): Bundle {
80     val bundle = Bundle()
81     val jsonIterator = jsonObject.keys()
82     while (jsonIterator.hasNext()) {
83       val key = jsonIterator.next()
84       val value = jsonObject[key]
85       if (value === JSONObject.NULL) {
86         // Null is not supported.
87         continue
88       }
89 
90       // Special case JSONObject as it's one way, on the return it would be Bundle.
91       if (value is JSONObject) {
92         bundle.putBundle(key, convertToBundle(value))
93         continue
94       }
95       val setter =
96         SETTERS[value.javaClass]
97           ?: throw IllegalArgumentException("Unsupported type: " + value.javaClass)
98       setter.setOnBundle(bundle, key, value)
99     }
100     return bundle
101   }
102 
103   interface Setter {
setOnBundlenull104     @Throws(JSONException::class) fun setOnBundle(bundle: Bundle, key: String, value: Any)
105 
106     @Throws(JSONException::class) fun setOnJSON(json: JSONObject, key: String, value: Any)
107   }
108 
109   init {
110     SETTERS[java.lang.Boolean::class.java] =
111       object : Setter {
112         @Throws(JSONException::class)
113         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
114           bundle.putBoolean(key, value as Boolean)
115         }
116 
117         @Throws(JSONException::class)
118         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
119           json.put(key, value)
120         }
121       }
122     SETTERS[java.lang.Integer::class.java] =
123       object : Setter {
124         @Throws(JSONException::class)
125         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
126           bundle.putInt(key, value as Int)
127         }
128 
129         @Throws(JSONException::class)
130         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
131           json.put(key, value)
132         }
133       }
134     SETTERS[java.lang.Long::class.java] =
135       object : Setter {
136         @Throws(JSONException::class)
137         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
138           bundle.putLong(key, value as Long)
139         }
140 
141         @Throws(JSONException::class)
142         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
143           json.put(key, value)
144         }
145       }
146     SETTERS[java.lang.Double::class.java] =
147       object : Setter {
148         @Throws(JSONException::class)
149         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
150           bundle.putDouble(key, value as Double)
151         }
152 
153         @Throws(JSONException::class)
154         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
155           json.put(key, value)
156         }
157       }
158     SETTERS[String::class.java] =
159       object : Setter {
160         @Throws(JSONException::class)
161         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
162           bundle.putString(key, value as String)
163         }
164 
165         @Throws(JSONException::class)
166         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
167           json.put(key, value)
168         }
169       }
170     SETTERS[Array<String>::class.java] =
171       object : Setter {
172         @Throws(JSONException::class)
173         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
174           throw IllegalArgumentException("Unexpected type from JSON")
175         }
176 
177         @Throws(JSONException::class)
178         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
179           val jsonArray = JSONArray()
180           for (stringValue in value as Array<String>) {
181             jsonArray.put(stringValue)
182           }
183           json.put(key, jsonArray)
184         }
185       }
186     SETTERS[JSONArray::class.java] =
187       object : Setter {
188         @Throws(JSONException::class)
189         override fun setOnBundle(bundle: Bundle, key: String, value: Any) {
190           val jsonArray = value as JSONArray
191           val stringArrayList = ArrayList<String>()
192           // Empty list, can't even figure out the type, assume an ArrayList<String>
193           if (jsonArray.length() == 0) {
194             bundle.putStringArrayList(key, stringArrayList)
195             return
196           }
197 
198           // Only strings are supported for now
199           for (i in 0 until jsonArray.length()) {
200             val current = jsonArray[i]
201             if (current is String) {
202               stringArrayList.add(current)
203             } else {
204               throw IllegalArgumentException("Unexpected type in an array: " + current.javaClass)
205             }
206           }
207           bundle.putStringArrayList(key, stringArrayList)
208         }
209 
210         @Throws(JSONException::class)
211         override fun setOnJSON(json: JSONObject, key: String, value: Any) {
212           throw IllegalArgumentException("JSONArray's are not supported in bundles.")
213         }
214       }
215   }
216 }
217