diff --git a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt index 900c73c5a3..ede822f0de 100644 --- a/examples/DemoRunner/Builds/Android/app/CMakeLists.txt +++ b/examples/DemoRunner/Builds/Android/app/CMakeLists.txt @@ -1016,6 +1016,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" @@ -3082,6 +3084,8 @@ set_source_files_properties( "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj index a56f53e087..1db5c45c96 100644 --- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj @@ -1313,6 +1313,9 @@ true + + true + true @@ -3332,6 +3335,7 @@ + diff --git a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters index c7f46a60be..9a235c7283 100644 --- a/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2017/DemoRunner_App.vcxproj.filters @@ -1930,6 +1930,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -5196,6 +5199,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj index ed44a0263b..a42fe6a8ec 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj @@ -1313,6 +1313,9 @@ true + + true + true @@ -3332,6 +3335,7 @@ + diff --git a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters index 8ea21b802c..e755256d7a 100644 --- a/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2019/DemoRunner_App.vcxproj.filters @@ -1930,6 +1930,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -5196,6 +5199,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj index bb93ee018a..2ac4138911 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj @@ -1313,6 +1313,9 @@ true + + true + true @@ -3332,6 +3335,7 @@ + diff --git a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters index 922f3319b5..c0383e7013 100644 --- a/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters +++ b/examples/DemoRunner/Builds/VisualStudio2022/DemoRunner_App.vcxproj.filters @@ -1930,6 +1930,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -5196,6 +5199,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt index fd039e8652..1f929bf994 100644 --- a/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPerformanceTest/Builds/Android/app/CMakeLists.txt @@ -892,6 +892,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" @@ -2658,6 +2660,8 @@ set_source_files_properties( "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj index b30591fbc8..a0fb61a960 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj @@ -1153,6 +1153,9 @@ true + + true + true @@ -2888,6 +2891,7 @@ + diff --git a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters index 2e0c11fcd1..c4870e885a 100644 --- a/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters +++ b/extras/AudioPerformanceTest/Builds/VisualStudio2022/AudioPerformanceTest_App.vcxproj.filters @@ -1639,6 +1639,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4476,6 +4479,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt index 3d86aba9be..f862d52a88 100644 --- a/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt +++ b/extras/AudioPluginHost/Builds/Android/app/CMakeLists.txt @@ -925,6 +925,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" @@ -2844,6 +2846,8 @@ set_source_files_properties( "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj index 7f7009fed5..6d98bca894 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj @@ -1161,6 +1161,9 @@ true + + true + true @@ -3068,6 +3071,7 @@ + diff --git a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters index 6412eaaf0e..4328bd6980 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2017/AudioPluginHost_App.vcxproj.filters @@ -1714,6 +1714,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4752,6 +4755,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj index 8e4d3b2c39..bdb62a984e 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj @@ -1161,6 +1161,9 @@ true + + true + true @@ -3068,6 +3071,7 @@ + diff --git a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters index fab70583a2..51192816ed 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2019/AudioPluginHost_App.vcxproj.filters @@ -1714,6 +1714,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4752,6 +4755,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj index a60475887e..da3cfab86d 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj @@ -1161,6 +1161,9 @@ true + + true + true @@ -3068,6 +3071,7 @@ + diff --git a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters index a1354ba663..374d5b4ee6 100644 --- a/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters +++ b/extras/AudioPluginHost/Builds/VisualStudio2022/AudioPluginHost_App.vcxproj.filters @@ -1714,6 +1714,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4752,6 +4755,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj index 111da4d5a0..efced8d6f4 100644 --- a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj +++ b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj @@ -220,6 +220,9 @@ true + + true + true @@ -528,6 +531,7 @@ + diff --git a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj.filters b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj.filters index 89603e456a..9c939bbb83 100644 --- a/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj.filters +++ b/extras/BinaryBuilder/Builds/VisualStudio2022/BinaryBuilder_ConsoleApp.vcxproj.filters @@ -160,6 +160,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -558,6 +561,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt index 41381d1f81..ddcb2c4cac 100644 --- a/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt +++ b/extras/NetworkGraphicsDemo/Builds/Android/app/CMakeLists.txt @@ -896,6 +896,8 @@ add_library( ${BINARY_NAME} "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" @@ -2742,6 +2744,8 @@ set_source_files_properties( "../../../../../modules/juce_core/javascript/juce_JSON.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation.h" "../../../../../modules/juce_core/javascript/juce_JSONSerialisation_test.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.cpp" + "../../../../../modules/juce_core/javascript/juce_JSONUtils.h" "../../../../../modules/juce_core/logging/juce_FileLogger.cpp" "../../../../../modules/juce_core/logging/juce_FileLogger.h" "../../../../../modules/juce_core/logging/juce_Logger.cpp" diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj index 38228cb131..f01c042d9a 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj @@ -1153,6 +1153,9 @@ true + + true + true @@ -2979,6 +2982,7 @@ + diff --git a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters index 8b36f6bdf8..8efe2dcdbd 100644 --- a/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters +++ b/extras/NetworkGraphicsDemo/Builds/VisualStudio2022/NetworkGraphicsDemo_App.vcxproj.filters @@ -1669,6 +1669,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4617,6 +4620,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj index e5b0dc5d3f..7a82b45dca 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj @@ -356,6 +356,9 @@ true + + true + true @@ -1821,6 +1824,7 @@ + diff --git a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters index 2c6d9092d7..66c254e7a2 100644 --- a/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2017/Projucer_App.vcxproj.filters @@ -637,6 +637,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -2631,6 +2634,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj index 3aa9a26508..8ceca47eae 100644 --- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj @@ -356,6 +356,9 @@ true + + true + true @@ -1821,6 +1824,7 @@ + diff --git a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters index 62bf8c6eb6..96316102a1 100644 --- a/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2019/Projucer_App.vcxproj.filters @@ -637,6 +637,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -2631,6 +2634,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj index f4bd7575a3..a543d57519 100644 --- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj +++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj @@ -356,6 +356,9 @@ true + + true + true @@ -1821,6 +1824,7 @@ + diff --git a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters index 649e69130c..879f8c7344 100644 --- a/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters +++ b/extras/Projucer/Builds/VisualStudio2022/Projucer_App.vcxproj.filters @@ -637,6 +637,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -2631,6 +2634,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj index 4b1fee2c6c..b980d5f172 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj @@ -1169,6 +1169,9 @@ true + + true + true @@ -3134,6 +3137,7 @@ + diff --git a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters index 4dfd10ecb3..4979cae04f 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2017/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1738,6 +1738,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4842,6 +4845,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj index e6cacf8e8b..b5357458f4 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj @@ -1169,6 +1169,9 @@ true + + true + true @@ -3134,6 +3137,7 @@ + diff --git a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters index 1cee45cd03..fafb374c90 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2019/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1738,6 +1738,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4842,6 +4845,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj index d98f2d81f3..0e89de1f5b 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj @@ -1169,6 +1169,9 @@ true + + true + true @@ -3134,6 +3137,7 @@ + diff --git a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters index 513b91f96f..e3710f917c 100644 --- a/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters +++ b/extras/UnitTestRunner/Builds/VisualStudio2022/UnitTestRunner_ConsoleApp.vcxproj.filters @@ -1738,6 +1738,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4842,6 +4845,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj index e0876552a7..05bbebd5b0 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj @@ -1152,6 +1152,9 @@ true + + true + true @@ -2955,6 +2958,7 @@ + diff --git a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters index 619101a3b3..a0e2a5a947 100644 --- a/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters +++ b/extras/WindowsDLL/Builds/VisualStudio2022/WindowsDLL_StaticLibrary.vcxproj.filters @@ -1666,6 +1666,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging @@ -4584,6 +4587,9 @@ JUCE Modules\juce_core\javascript + + JUCE Modules\juce_core\javascript + JUCE Modules\juce_core\logging diff --git a/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp b/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp index 780ececdb6..3357c1f079 100644 --- a/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp +++ b/modules/juce_core/javascript/juce_JSONSerialisation_test.cpp @@ -398,29 +398,32 @@ public: "hello world", { 5, 6, 7 }, { { "foo", 4 }, { "bar", 5 } } }), - makeObject ({ { "__version__", 2 }, - { "a", 7 }, - { "b", "hello world" }, - { "c", Array { 5, 6, 7 } }, - { "d", Array { makeObject ({ { "first", "bar" }, { "second", 5 } }), - makeObject ({ { "first", "foo" }, { "second", 4 } }) } } })); + JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 7 }, + { "b", "hello world" }, + { "c", Array { 5, 6, 7 } }, + { "d", + Array { JSONUtils::makeObject ({ { "first", "bar" }, + { "second", 5 } }), + JSONUtils::makeObject ({ { "first", "foo" }, + { "second", 4 } }) } } })); expectDeepEqual (ToVar::convert (TypeWithInternalUnifiedSerialisation { 7.89, 4.321f, "custom string", { "foo", "bar", "baz" } }), - makeObject ({ { "__version__", 5 }, - { "a", 7.89 }, - { "b", 4.321f }, - { "c", "custom string" }, - { "d", Array { "foo", "bar", "baz" } } })); + JSONUtils::makeObject ({ { "__version__", 5 }, + { "a", 7.89 }, + { "b", 4.321f }, + { "c", "custom string" }, + { "d", Array { "foo", "bar", "baz" } } })); expectDeepEqual (ToVar::convert (TypeWithExternalSplitSerialisation { "string", { 1, 2, 3 } }), - makeObject ({ { "__version__", 10 }, - { "a", makeObject ({ { "engaged", true }, { "value", "string" } }) }, - { "b", Array { "0x1", "0x2", "0x3" } } })); + JSONUtils::makeObject ({ { "__version__", 10 }, + { "a", JSONUtils::makeObject ({ { "engaged", true }, { "value", "string" } }) }, + { "b", Array { "0x1", "0x2", "0x3" } } })); expectDeepEqual (ToVar::convert (TypeWithInternalSplitSerialisation { "string", { 16, 32, 48 } }), - makeObject ({ { "__version__", 1 }, - { "a", "string" }, - { "b", Array { "0x10", "0x20", "0x30" } } })); + JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", "string" }, + { "b", Array { "0x10", "0x20", "0x30" } } })); expect (ToVar::convert (TypeWithBrokenObjectSerialisation { 1, 2 }) == std::nullopt); expect (ToVar::convert (TypeWithBrokenPrimitiveSerialisation { 1, 2 }) == std::nullopt); @@ -429,69 +432,69 @@ public: expect (ToVar::convert (TypeWithBrokenDynamicSerialisation { std::vector (10) }) == std::nullopt); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }), - makeObject ({ { "__version__", 3 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 }, - { "d", 4 } })); - expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options{}.withVersionIncluded (false)), - makeObject ({ { "a", 1 }, - { "b", 2 }, - { "c", 3 }, - { "d", 4 } })); + JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withVersionIncluded (false)), + JSONUtils::makeObject ({ { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); // Requested explicit version is higher than the type's declared version - expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options{}.withExplicitVersion (4)), + expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (4)), std::nullopt); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (3)), - makeObject ({ { "__version__", 3 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 }, - { "d", 4 } })); + JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (2)), - makeObject ({ { "__version__", 2 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 } })); + JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 } })); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (1)), - makeObject ({ { "__version__", 1 }, - { "a", 1 }, - { "b", 2 } })); + JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", 1 }, + { "b", 2 } })); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (0)), - makeObject ({ { "__version__", 0 }, - { "a", 1 } })); + JSONUtils::makeObject ({ { "__version__", 0 }, + { "a", 1 } })); expectDeepEqual (ToVar::convert (TypeWithVersionedSerialisation { 1, 2, 3, 4 }, ToVar::Options {}.withExplicitVersion (std::nullopt)), - makeObject ({ { "a", 1 } })); + JSONUtils::makeObject ({ { "a", 1 } })); expectDeepEqual (ToVar::convert (TypeWithRawVarLast { 200, "success", true }), - makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); + JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); expectDeepEqual (ToVar::convert (TypeWithRawVarLast { 200, "success", - makeObject ({ { "status", 123.456 }, - { "message", "failure" }, - { "extended", true } }) }), - makeObject ({ { "status", 200 }, - { "message", "success" }, - { "extended", makeObject ({ { "status", 123.456 }, - { "message", "failure" }, - { "extended", true } }) } })); + JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) }), + JSONUtils::makeObject ({ { "status", 200 }, + { "message", "success" }, + { "extended", JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) } })); expectDeepEqual (ToVar::convert (TypeWithRawVarFirst { 200, "success", true }), - makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); + JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", true } })); expectDeepEqual (ToVar::convert (TypeWithRawVarFirst { 200, "success", - makeObject ({ { "status", 123.456 }, - { "message", "failure" }, - { "extended", true } }) }), - makeObject ({ { "status", 200 }, - { "message", "success" }, - { "extended", makeObject ({ { "status", 123.456 }, - { "message", "failure" }, - { "extended", true } }) } })); + JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) }), + JSONUtils::makeObject ({ { "status", 200 }, + { "message", "success" }, + { "extended", JSONUtils::makeObject ({ { "status", 123.456 }, + { "message", "failure" }, + { "extended", true } }) } })); - const auto payload = makeObject ({ { "foo", 1 }, { "bar", 2 } }); + const auto payload = JSONUtils::makeObject ({ { "foo", 1 }, { "bar", 2 } }); expectDeepEqual (ToVar::convert (TypeWithInnerVar { 404, payload }), - makeObject ({ { "eventId", 404 }, { "payload", payload } })); + JSONUtils::makeObject ({ { "eventId", 404 }, { "payload", payload } })); } beginTest ("FromVar"); @@ -505,34 +508,37 @@ public: expect (FromVar::convert (JSON::fromString ("6")) == 6); expect (FromVar::convert (JSON::fromString ("\"hello world\"")) == "hello world"); expect (FromVar::convert> (JSON::fromString ("[1,2,3]")) == std::vector { 1, 2, 3 }); - expect (FromVar::convert (makeObject ({ { "__version__", 2 }, - { "a", 7 }, - { "b", "hello world" }, - { "c", Array { 5, 6, 7 } }, - { "d", Array { makeObject ({ { "first", "bar" }, { "second", 5 } }), - makeObject ({ { "first", "foo" }, { "second", 4 } }) } } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 7 }, + { "b", "hello world" }, + { "c", Array { 5, 6, 7 } }, + { "d", + Array { JSONUtils::makeObject ({ { "first", "bar" }, + { "second", 5 } }), + JSONUtils::makeObject ({ { "first", "foo" }, + { "second", 4 } }) } } })) == TypeWithExternalUnifiedSerialisation { 7, "hello world", { 5, 6, 7 }, { { "foo", 4 }, { "bar", 5 } } }); - expect (FromVar::convert (makeObject ({ { "__version__", 5 }, - { "a", 7.89 }, - { "b", 4.321f }, - { "c", "custom string" }, - { "d", Array { "foo", "bar", "baz" } } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 5 }, + { "a", 7.89 }, + { "b", 4.321f }, + { "c", "custom string" }, + { "d", Array { "foo", "bar", "baz" } } })) == TypeWithInternalUnifiedSerialisation { 7.89, 4.321f, "custom string", { "foo", "bar", "baz" } }); - expect (FromVar::convert (makeObject ({ { "__version__", 10 }, - { "a", makeObject ({ { "engaged", true }, { "value", "string" } }) }, - { "b", Array { "0x1", "0x2", "0x3" } } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 10 }, + { "a", JSONUtils::makeObject ({ { "engaged", true }, { "value", "string" } }) }, + { "b", Array { "0x1", "0x2", "0x3" } } })) == TypeWithExternalSplitSerialisation { "string", { 1, 2, 3 } }); - expect (FromVar::convert (makeObject ({ { "__version__", 1 }, - { "a", "string" }, - { "b", Array { "0x10", "0x20", "0x30" } } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", "string" }, + { "b", Array { "0x10", "0x20", "0x30" } } })) == TypeWithInternalSplitSerialisation { "string", { 16, 32, 48 } }); expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); @@ -541,47 +547,48 @@ public: expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); expect (FromVar::convert (JSON::fromString ("null")) == std::nullopt); - expect (FromVar::convert (makeObject ({ { "a", 7.89 }, - { "b", 4.321f } })) == std::nullopt); + expect (FromVar::convert (JSONUtils::makeObject ({ { "a", 7.89 }, + { "b", 4.321f } })) + == std::nullopt); - expect (FromVar::convert (makeObject ({ { "__version__", 3 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 }, - { "d", 4 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 3 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })) == TypeWithVersionedSerialisation { 1, 2, 3, 4 }); - expect (FromVar::convert (makeObject ({ { "__version__", 4 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 }, - { "d", 4 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 4 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 }, + { "d", 4 } })) == TypeWithVersionedSerialisation { 1, 2, 3, 4 }); - expect (FromVar::convert (makeObject ({ { "__version__", 2 }, - { "a", 1 }, - { "b", 2 }, - { "c", 3 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 2 }, + { "a", 1 }, + { "b", 2 }, + { "c", 3 } })) == TypeWithVersionedSerialisation { 1, 2, 3, 0 }); - expect (FromVar::convert (makeObject ({ { "__version__", 1 }, - { "a", 1 }, - { "b", 2 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 1 }, + { "a", 1 }, + { "b", 2 } })) == TypeWithVersionedSerialisation { 1, 2, 0, 0 }); - expect (FromVar::convert (makeObject ({ { "__version__", 0 }, - { "a", 1 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "__version__", 0 }, + { "a", 1 } })) == TypeWithVersionedSerialisation { 1, 0, 0, 0 }); - expect (FromVar::convert (makeObject ({ { "a", 1 } })) + expect (FromVar::convert (JSONUtils::makeObject ({ { "a", 1 } })) == TypeWithVersionedSerialisation { 1, 0, 0, 0 }); - const auto raw = makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", "another string" } }); + const auto raw = JSONUtils::makeObject ({ { "status", 200 }, { "message", "success" }, { "extended", "another string" } }); expect (FromVar::convert (raw) == TypeWithRawVarLast { 200, "success", "another string" }); expect (FromVar::convert (raw) == TypeWithRawVarFirst { 200, "success", "another string" }); - const var payloads[] { makeObject ({ { "foo", 1 }, { "bar", 2 } }), + const var payloads[] { JSONUtils::makeObject ({ { "foo", 1 }, { "bar", 2 } }), var (Array { 1, 2 }), var() }; for (const auto& payload : payloads) { - const auto objectWithPayload = makeObject ({ { "eventId", 404 }, { "payload", payload } }); + const auto objectWithPayload = JSONUtils::makeObject ({ { "eventId", 404 }, { "payload", payload } }); expect (FromVar::convert (objectWithPayload) == TypeWithInnerVar { 404, payload }); } } @@ -593,57 +600,12 @@ private: expect (deepEqual (a, b), a.has_value() && b.has_value() ? JSON::toString (*a) + " != " + JSON::toString (*b) : String()); } - static var makeObject (const std::map& map) - { - auto obj = std::make_unique(); - - for (auto& [key, value] : map) - obj->setProperty (key, value); - - return obj.release(); - } - - static bool deepEqual (const DynamicObject& a, const DynamicObject& b) - { - if (a.getProperties().size() != b.getProperties().size()) - return false; - - for (const auto& [key, value] : a.getProperties()) - { - if (! b.hasProperty (key)) - return false; - - if (! deepEqual (value, b.getProperty (key))) - return false; - } - - return true; - } - - static bool deepEqual (const Array& a, const Array& b) - { - return std::equal (a.begin(), a.end(), b.begin(), b.end(), [] (const var& i, const var& j) { return deepEqual (i, j); }); - } - - static bool deepEqual (const var& a, const var& b) - { - if (auto* i = a.getDynamicObject()) - if (auto* j = b.getDynamicObject()) - return deepEqual (*i, *j); - - if (auto* i = a.getArray()) - if (auto* j = b.getArray()) - return deepEqual (*i, *j); - - return a == b; - } - static bool deepEqual (const std::optional& a, const std::optional& b) { - if (a.has_value() != b.has_value()) - return false; + if (a.has_value() && b.has_value()) + return JSONUtils::deepEqual (*a, *b); - return ! a.has_value() || deepEqual (*a, *b); + return a == b; } }; diff --git a/modules/juce_core/javascript/juce_JSONUtils.cpp b/modules/juce_core/javascript/juce_JSONUtils.cpp new file mode 100644 index 0000000000..36bdf1f6f0 --- /dev/null +++ b/modules/juce_core/javascript/juce_JSONUtils.cpp @@ -0,0 +1,215 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +var JSONUtils::makeObject (const std::map& source) +{ + auto result = std::make_unique(); + + for (const auto& [name, value] : source) + result->setProperty (name, value); + + return var (result.release()); +} + +var JSONUtils::makeObjectWithKeyFirst (const std::map& source, + Identifier key) +{ + auto result = std::make_unique(); + + if (const auto iter = source.find (key); iter != source.end()) + result->setProperty (key, iter->second); + + for (const auto& [name, value] : source) + if (name != key) + result->setProperty (name, value); + + return var (result.release()); +} + +std::optional JSONUtils::setPointer (const var& v, + String pointer, + const var& newValue) +{ + if (pointer.isEmpty()) + return newValue; + + if (! pointer.startsWith ("/")) + { + // This is not a well-formed JSON pointer + jassertfalse; + return {}; + } + + const auto findResult = pointer.indexOfChar (1, '/'); + const auto pos = findResult < 0 ? pointer.length() : findResult; + const String head (pointer.begin() + 1, pointer.begin() + pos); + const String tail (pointer.begin() + pos, pointer.end()); + + const auto unescaped = head.replace ("~1", "/").replace ("~0", "~"); + + if (auto* object = v.getDynamicObject()) + { + if (const auto newProperty = setPointer (object->getProperty (unescaped), tail, newValue)) + { + auto cloned = object->clone(); + cloned->setProperty (unescaped, *newProperty); + return var (cloned.release()); + } + } + else if (auto* array = v.getArray()) + { + const auto index = [&]() -> size_t + { + if (unescaped == "-") + return (size_t) array->size(); + + if (unescaped == "0") + return 0; + + if (! unescaped.startsWith ("0")) + return (size_t) unescaped.getLargeIntValue(); + + return std::numeric_limits::max(); + }(); + + if (const auto newIndex = setPointer ((*array)[(int) index], tail, newValue)) + { + auto copied = *array; + + if ((int) index == copied.size()) + copied.add ({}); + + if (isPositiveAndBelow (index, copied.size())) + { + copied.getReference ((int) index) = *newIndex; + return var (copied); + } + } + } + + return {}; +} + +bool JSONUtils::deepEqual (const var& a, const var& b) +{ + const auto compareObjects = [] (const DynamicObject& x, const DynamicObject& y) + { + if (x.getProperties().size() != y.getProperties().size()) + return false; + + for (const auto& [key, value] : x.getProperties()) + { + if (! y.hasProperty (key)) + return false; + + if (! deepEqual (value, y.getProperty (key))) + return false; + } + + return true; + }; + + if (auto* i = a.getDynamicObject()) + if (auto* j = b.getDynamicObject()) + return compareObjects (*i, *j); + + if (auto* i = a.getArray()) + if (auto* j = b.getArray()) + return std::equal (i->begin(), i->end(), j->begin(), j->end(), [] (const var& x, const var& y) { return deepEqual (x, y); }); + + return a == b; +} + +//============================================================================== +//============================================================================== +#if JUCE_UNIT_TESTS + +class JSONUtilsTests : public UnitTest +{ +public: + JSONUtilsTests() : UnitTest ("JSONUtils", UnitTestCategories::json) {} + + void runTest() override + { + beginTest ("JSON pointers"); + { + const auto obj = JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] } + })"); + expectDeepEqual (JSONUtils::setPointer (obj, "", "hello world"), var ("hello world")); + expectDeepEqual (JSONUtils::setPointer (obj, "/lfoWaveform/foobar", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/foo", 2), JSON::parse (R"({"foo":2,"bar":1})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":0,"bar":1})"), "/baz", 2), JSON::parse (R"({"foo":0,"bar":1,"baz":2})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"foo":{},"bar":{}})"), "/foo/bar", 2), JSON::parse (R"({"foo":{"bar":2},"bar":{}})")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/01", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/10", "str"), std::nullopt); + expectDeepEqual (JSONUtils::setPointer (obj, "/lfoSpeed", 10), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 10 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"([0,1,2])"), "/0", "bang"), JSON::parse (R"(["bang",1,2])")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"/":"fizz"})"), "/~1", "buzz"), JSON::parse (R"({"/":"buzz"})")); + expectDeepEqual (JSONUtils::setPointer (JSON::parse (R"({"~":"fizz"})"), "/~0", "buzz"), JSON::parse (R"({"~":"buzz"})")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/rates/0", 80), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [80,67,95,60], "levels": [50,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/0", 80), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [80,50,50,50] } + })")); + expectDeepEqual (JSONUtils::setPointer (obj, "/pitchEnvelope/levels/-", 100), JSON::parse (R"({ "name": "PIANO 4" + , "lfoSpeed": 30 + , "lfoWaveform": "triangle" + , "pitchEnvelope": { "rates": [94,67,95,60], "levels": [50,50,50,50,100] } + })")); + } + } + + void expectDeepEqual (const std::optional& a, const std::optional& b) + { + expect (deepEqual (a, b), a.has_value() && b.has_value() ? JSON::toString (*a) + " != " + JSON::toString (*b) : String()); + } + + static bool deepEqual (const std::optional& a, const std::optional& b) + { + if (a.has_value() && b.has_value()) + return JSONUtils::deepEqual (*a, *b); + + return a == b; + } +}; + +static JSONUtilsTests jsonUtilsTests; + +#endif + +} // namespace juce diff --git a/modules/juce_core/javascript/juce_JSONUtils.h b/modules/juce_core/javascript/juce_JSONUtils.h new file mode 100644 index 0000000000..52ab5e0662 --- /dev/null +++ b/modules/juce_core/javascript/juce_JSONUtils.h @@ -0,0 +1,67 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2022 - Raw Material Software Limited + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +/** + A mini namespace to hold utility functions for working with juce::vars. + + @tags{Core} +*/ +struct JSONUtils +{ + /** No constructor. */ + JSONUtils() = delete; + + /** Given a JSON array/object 'v', a string representing a JSON pointer, + and a new property value 'newValue', returns a copy of 'v' where the + property or array index referenced by the pointer has been set to 'newValue'. + + If the pointer cannot be followed, due to referencing missing array indices + or fields, then this returns nullopt. + + For more details, check the JSON Pointer RFC 6901: + https://datatracker.ietf.org/doc/html/rfc6901 + */ + static std::optional setPointer (const var& v, String pointer, const var& newValue); + + /** Converts the provided key/value pairs into a JSON object. */ + static var makeObject (const std::map& source); + + /** Converts the provided key/value pairs into a JSON object with the provided + key at the first position in the object. + + This is useful because the MIDI-CI spec requires that certain fields (e.g. + status) should be placed at the beginning of a MIDI-CI header. + */ + static var makeObjectWithKeyFirst (const std::map& source, Identifier key); + + /** Returns true if and only if the contents of a match the contents of b. + + Unlike var::operator==, this will recursively check that contained DynamicObject and Array + instances compare equal. + */ + static bool deepEqual (const var& a, const var& b); +}; + +} // namespace juce diff --git a/modules/juce_core/juce_core.cpp b/modules/juce_core/juce_core.cpp index 0e6b23fb37..c187355fd7 100644 --- a/modules/juce_core/juce_core.cpp +++ b/modules/juce_core/juce_core.cpp @@ -178,6 +178,7 @@ #include "unit_tests/juce_UnitTest.cpp" #include "containers/juce_Variant.cpp" #include "javascript/juce_JSON.cpp" +#include "javascript/juce_JSONUtils.cpp" #include "javascript/juce_Javascript.cpp" #include "containers/juce_DynamicObject.cpp" #include "xml/juce_XmlDocument.cpp" diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index 011dd00c68..bd4d52856e 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -309,6 +309,7 @@ JUCE_END_IGNORE_WARNINGS_MSVC #include "streams/juce_FileInputSource.h" #include "logging/juce_FileLogger.h" #include "javascript/juce_JSON.h" +#include "javascript/juce_JSONUtils.h" #include "serialisation/juce_Serialisation.h" #include "javascript/juce_JSONSerialisation.h" #include "javascript/juce_Javascript.h"