From 7ad307631562806f6fe198146d5fce072af61485 Mon Sep 17 00:00:00 2001 From: YuTian <2953516620@qq.com> Date: Sat, 3 Aug 2024 10:05:51 +0800 Subject: [PATCH] update --- .idea/artifacts/McLiveAPI.xml | 2 +- .idea/jarRepositories.xml | 15 + McLiveAPI.iml | 4 +- lib/PixelLivePlugin.jar | Bin 48949 -> 45566 bytes pom.xml | 32 +- .../liveroom/LiveRoomWatcher.java | 2 - .../livemutually/manager/GiftManager.java | 242 ++ .../manager/KSLiveRoomManager.java | 14 +- .../livemutually/manager/RankManager.java | 115 + .../yutian/livemutually/manager/UserData.java | 39 - .../livemutually/manager/UserManager.java | 20 + .../wss/KSAPILiveRoomWatcher.java | 6 +- src/main/java/com/io/yutian/mclive/Main.java | 197 +- .../manager => mclive/data}/GiftData.java | 4 +- .../com/io/yutian/mclive/data/GiftManage.java | 243 -- .../io/yutian/mclive/data/IPGeolocation.java | 6 +- .../com/io/yutian/mclive/data/UserData.java | 39 + .../yutian/mclive/data/UserResourceData.java | 29 + .../mclive/data/database/SqlManager.java | 46 +- .../yutian/mclive/event/LiveChatEvents.java | 8 +- .../mclive/event/LiveConnectEvents.java | 11 +- .../yutian/mclive/event/LiveEnterEvents.java | 8 +- .../yutian/mclive/event/LiveFollowEvents.java | 8 +- .../yutian/mclive/event/LiveGiftEvents.java | 8 +- .../yutian/mclive/event/LiveLikeEvents.java | 8 +- .../com/io/yutian/mclive/event/ZhuboAPI.java | 23 +- .../yutian/mclive/listener/JoinGameRoom.java | 28 +- .../yutian/mclive/listener/LiveAdminGui.java | 272 +- .../yutian/mclive/listener/RankListener.java | 15 + .../io/yutian/mclive/listener/SoundsMenu.java | 113 +- .../com/io/yutian/mclive/live/LiveEvent.java | 35 +- .../com/io/yutian/mclive/live/ModEvent.java | 6 +- .../com/io/yutian/mclive/util/ConfigYml.java | 22 +- .../com/io/yutian/mclive/util/FileUtil.java | 34 + .../io/yutian/mclive/util/MessageUtil.java | 79 +- .../com/io/yutian/mclive/util/SqlUtil.java | 4 +- .../java/com/io/yutian/verify/AESUtil.java | 30 +- .../com/io/yutian/verify/VerifyHandler.java | 13 +- src/main/java/json/JSONArray.java | 639 ++-- src/main/java/json/JSONException.java | 20 +- src/main/java/json/JSONObject.java | 3110 ++++++++--------- src/main/java/json/JSONPointer.java | 243 +- src/main/java/json/JSONPointerException.java | 2 +- src/main/java/json/JSONPropertyIgnore.java | 3 +- src/main/java/json/JSONString.java | 1 + src/main/java/json/JSONStringer.java | 2 + src/main/java/json/JSONTokener.java | 288 +- src/main/java/json/JSONWriter.java | 447 +-- .../yutian/livemutually/liveroom/Chat.class | Bin 228 -> 228 bytes .../yutian/livemutually/liveroom/Follow.class | Bin 191 -> 191 bytes .../yutian/livemutually/liveroom/Like.class | Bin 209 -> 209 bytes .../liveroom/LiveRoomWatcher.class | Bin 3027 -> 3027 bytes .../yutian/livemutually/liveroom/User.class | Bin 184 -> 184 bytes .../manager/KSLiveRoomManager.class | Bin 1184 -> 1310 bytes .../wss/KSAPILiveRoomWatcher.class | Bin 4086 -> 4086 bytes .../livemutually/wss/KuaiShouChat.class | Bin 894 -> 894 bytes .../livemutually/wss/KuaiShouLike.class | Bin 836 -> 836 bytes .../livemutually/wss/KuaiShouUser.class | Bin 619 -> 619 bytes .../classes/com/io/yutian/mclive/Main.class | Bin 12611 -> 13328 bytes .../yutian/mclive/event/LiveChatEvents.class | Bin 1303 -> 1303 bytes .../yutian/mclive/event/LiveEnterEvents.class | Bin 1123 -> 1123 bytes .../mclive/event/LiveFollowEvents.class | Bin 1126 -> 1126 bytes .../yutian/mclive/event/LiveGiftEvents.class | Bin 1428 -> 1428 bytes .../yutian/mclive/event/LiveLikeEvents.class | Bin 1248 -> 1248 bytes .../com/io/yutian/mclive/event/ZhuboAPI.class | Bin 1748 -> 1769 bytes .../com/io/yutian/verify/AESUtil.class | Bin 6055 -> 6055 bytes .../io/yutian/verify/PluginVerifyResult.class | Bin 1642 -> 1642 bytes .../com/io/yutian/verify/VerifyHandler.class | Bin 3754 -> 3754 bytes target/classes/json/JSONArray.class | Bin 18677 -> 18679 bytes target/classes/json/JSONException.class | Bin 764 -> 764 bytes target/classes/json/JSONObject$Null.class | Bin 792 -> 792 bytes target/classes/json/JSONObject.class | Bin 35141 -> 35142 bytes target/classes/json/JSONPointer$Builder.class | Bin 1224 -> 1224 bytes target/classes/json/JSONPointer.class | Bin 5555 -> 5555 bytes .../classes/json/JSONPointerException.class | Bin 599 -> 599 bytes target/classes/json/JSONPropertyIgnore.class | Bin 434 -> 434 bytes target/classes/json/JSONPropertyName.class | Bin 461 -> 461 bytes target/classes/json/JSONString.class | Bin 152 -> 152 bytes target/classes/json/JSONStringer.class | Bin 604 -> 604 bytes target/classes/json/JSONTokener.class | Bin 7289 -> 7289 bytes target/classes/json/JSONWriter.class | Bin 6172 -> 6172 bytes 81 files changed, 3302 insertions(+), 3233 deletions(-) create mode 100644 src/main/java/com/io/yutian/livemutually/manager/GiftManager.java create mode 100644 src/main/java/com/io/yutian/livemutually/manager/RankManager.java delete mode 100644 src/main/java/com/io/yutian/livemutually/manager/UserData.java create mode 100644 src/main/java/com/io/yutian/livemutually/manager/UserManager.java rename src/main/java/com/io/yutian/{livemutually/manager => mclive/data}/GiftData.java (81%) delete mode 100644 src/main/java/com/io/yutian/mclive/data/GiftManage.java create mode 100644 src/main/java/com/io/yutian/mclive/data/UserData.java create mode 100644 src/main/java/com/io/yutian/mclive/data/UserResourceData.java create mode 100644 src/main/java/com/io/yutian/mclive/listener/RankListener.java create mode 100644 src/main/java/com/io/yutian/mclive/util/FileUtil.java diff --git a/.idea/artifacts/McLiveAPI.xml b/.idea/artifacts/McLiveAPI.xml index d5c0dce..a988fca 100644 --- a/.idea/artifacts/McLiveAPI.xml +++ b/.idea/artifacts/McLiveAPI.xml @@ -1,6 +1,6 @@ - $PROJECT_DIR$/out/artifacts/McLiveAPI + $PROJECT_DIR$/../../../Desktop/1.18.1s/plugins diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml index 712ab9d..0f87322 100644 --- a/.idea/jarRepositories.xml +++ b/.idea/jarRepositories.xml @@ -6,6 +6,21 @@ diff --git a/lib/PixelLivePlugin.jar b/lib/PixelLivePlugin.jar index e34e641bf6154b3aca13a1ac09ed59b5e1855b28..3dfb469303f5032409f2990fb2141abf54b8e63d 100644 GIT binary patch literal 45566 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 N!mIj=X9H+jupU;J6Q!F9 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 z zS%x7TaX2nK(D`gz*_W^X@CeD-JwM6-96l`oWBXsbcS!%O#cAqlYWMEHvSK;^Lr!&N zSyN|cLvvHnA5Hx8E}&#5v#LEJK-W-xCy(xBN*j7jmo`z|b>4}qo3LTo%1zc2U z|HB5w#JOwIJ5z@k#G0kmz$VSGcnxw=S79Oa3Job^aIz1Y-=#mpnJ|X$_ptCG&^7P} zC|Vz{A6sHB6DGc7r5mi@M}y@VNUHNDC!If;Gz5kSjGQ(1#|p|t4-R}L(HJlp|r{Gc(39L1N5~lrz@lbEKC)+=D#Yf@tfr5xHAa>-&Pm&_SZyvTPV&!CK zXw`Fn>u&A{TX?N`d%=$hb^xU$MHxv<8j3%Y#C?pMelDo9Zw6J~M5PoeeEiIcWC3z9 zd+LRwhE*f>g5#kYraM0$iwJmgI(Kf;ifB5=see=1aj1&B7cn_1q*A`eW z;m^!rBhfZQB^ep*lR>9k{RnT!c+A(gi~R-jKgz!b6G~cTv*v7Nnbf#@Sk9A3OOi@U z!b(X_zh#!0HJ=3zJ~p|HL(O^RZ;TZD1RZ+kDFn8!$hBr%T*)Ad!6x7sDCWrHmQeQ2Hz@`S1E`06XT;%QWmt8)`a&* zxzmvbwzij}krYNLtXprgBbc6QNqF~BqDZ1cG6a;49c!+khXN8A-?s-m%+~q`kebSH z!@uW_z zHYohi_r@rnA|-)nRS0||bQ|sTE0I`fi=v7}q5VzE@kdhpG4dr-ojDxMJpY2!0(PCr z4XbUK{Ia8K^`^I~%Di+Uaj-D;F}(~wE-LvJTg?l=MMVuT+Z3dMK~RBEfuNwEfaaoi zGyqr9|3w7AfPVO3eieHsbB33(zefR_I={#dMeqY<9xJTgri6x2Xm&Sjv{8mZ)JJT|b z8B`j8VF2*=qp94y);L5#m3g}WNpi4j8LqEkHpl$zeJbrZP zpG0ylHa36F_3KexKl0UnN-6nYa{V>Uug4$#u*QFin9XlAe|gH+?_>3AN%xPUwV%Re z|JQtf-LGGZivffDi!=d(S$`U;fA;I&OyaM#wSf8mMV!5VC;W>F<==njuZ5&P3UYtS zpwGXc`&pj*JK3)_oqwK7iub>f{aT6oJI$}9gnwSAhks4;zbTEs6aAVc`R7S&^Z%_s zf2u?Lo#)qJ>2JR)?l+!aGc13n`89(1BeMEaEV6!$v;L0z@jbx(>>mQS;(ihA?0!~O5D_&n`v0uoe_^33NP`3H z6d)i1z$+gL2uK)^-@pwdZ)E1;Y;5?>(%z2H$b(SO#>ViSy^|&3|Nh_JiO}NRI|ptC R1~)f1dc%KyOb-YS{SV2I7*hZM diff --git a/pom.xml b/pom.xml index 00a1443..419af90 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,4 @@ - + @@ -49,4 +49,34 @@ + + + papermc-repo + https://repo.papermc.io/repository/maven-public/ + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + public-rpg + https://repo.aurora-pixels.com/repository/public-rpg/ + + + + + + io.papermc.paper + paper-api + 1.18.2-R0.1-SNAPSHOT + provided + + + cn.hamster3.cdapi + CDTimeAPI + 1.0 + + + + \ No newline at end of file diff --git a/src/main/java/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.java b/src/main/java/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.java index cffe0da..0e54035 100644 --- a/src/main/java/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.java +++ b/src/main/java/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.java @@ -1,7 +1,5 @@ package com.io.yutian.livemutually.liveroom; -import org.bukkit.Bukkit; - import java.util.Objects; import java.util.function.Consumer; diff --git a/src/main/java/com/io/yutian/livemutually/manager/GiftManager.java b/src/main/java/com/io/yutian/livemutually/manager/GiftManager.java new file mode 100644 index 0000000..a82bef7 --- /dev/null +++ b/src/main/java/com/io/yutian/livemutually/manager/GiftManager.java @@ -0,0 +1,242 @@ +package com.io.yutian.livemutually.manager; + +import com.io.yutian.mclive.Main; +import com.io.yutian.mclive.data.database.SqlManager; +import com.io.yutian.mclive.util.ConfigYml; +import com.io.yutian.mclive.util.SqlUtil; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; + +public class GiftManager { + + private double totalMoney; + private LinkedHashMap giftMap = new LinkedHashMap<>(); + private SqlUtil sqlUtil; + private SqlManager sqlManager; + + public GiftManager() { + totalMoney = 0; + giftMap.put("小心心", 1); + giftMap.put("人气票", 1); + giftMap.put("闪耀星辰", 1); + giftMap.put("加油鸭", 1); + giftMap.put("比心兔兔", 1); + giftMap.put("热气球", 1); + giftMap.put("爱你哟", 1); + giftMap.put("天鹅之梦", 1); + giftMap.put("Thuglife", 1); + giftMap.put("粘人小狗", 1); + giftMap.put("粉丝团灯牌", 1); + giftMap.put("大啤酒", 2); + giftMap.put("玫瑰", 1); + giftMap.put("抖音", 1); + giftMap.put("称心如意", 1); + giftMap.put("你最好看", 2); + giftMap.put("助力票", 1); + giftMap.put("荣耀擂鼓", 99); + giftMap.put("宠粉季", 1); + giftMap.put("兔耳朵", 99); + giftMap.put("游戏手柄", 99); + giftMap.put("冰镇西瓜", 99); + giftMap.put("为你闪耀", 9); + giftMap.put("棒棒糖", 9); + giftMap.put("鲜花", 10); + giftMap.put("亲吻", 99); + giftMap.put("跑车", 1200); + giftMap.put("礼花筒", 199); + giftMap.put("捏捏小脸", 99); + giftMap.put("鹿仙子", 99); + giftMap.put("夏威夷花环", 99); + giftMap.put("爱的纸鹤", 99); + giftMap.put("送你花花", 49); + giftMap.put("女神花环", 99); + giftMap.put("鱼你一起", 99); + giftMap.put("真爱玫瑰", 366); + giftMap.put("为你举牌", 199); + giftMap.put("龙抬头", 99); + giftMap.put("花开烂漫", 466); + giftMap.put("比心", 199); + giftMap.put("真的爱你", 520); + giftMap.put("万象烟花", 688); + giftMap.put("私人飞机", 3000); + giftMap.put("浪漫烟花", 599); + giftMap.put("闪亮登场", 460); + giftMap.put("多喝热水", 126); + giftMap.put("一点心意", 266); + giftMap.put("荧光棒", 99); + giftMap.put("娶你回家", 599); + giftMap.put("掌上明珠", 888); + giftMap.put("摧残舞台", 899); + giftMap.put("星星点灯", 268); + giftMap.put("一束花开", 366); + giftMap.put("小傻猪", 299); + giftMap.put("环球旅行车", 650); + giftMap.put("爱的守护", 299); + giftMap.put("好运莲莲鸭", 299); + giftMap.put("日出相伴", 726); + giftMap.put("永生花", 520); + giftMap.put("纸短情长", 921); + giftMap.put("直升机", 2999); + giftMap.put("蝶·连理枝", 280); + giftMap.put("爱情树下", 599); + giftMap.put("灵龙现世", 600); + giftMap.put("爱心煎蛋", 99); + giftMap.put("夏日回忆", 1000); + giftMap.put("抖音1号", 10001); + giftMap.put("繁花秘语", 1314); + giftMap.put("ONE礼挑一", 299); + giftMap.put("重拳出击", 199); + giftMap.put("花落长亭", 1588); + giftMap.put("浪漫恋人", 1999); + giftMap.put("花海泛舟", 2800); + giftMap.put("豪华邮轮", 6000); + giftMap.put("环游世界", 3000); + giftMap.put("蝶·书中情", 750); + giftMap.put("带你去海边", 4500); + giftMap.put("蜜蜂叮叮", 1000); + giftMap.put("奇幻八音盒", 2399); + giftMap.put("光之祝福", 1999); + giftMap.put("消暑罐头车", 1500); + giftMap.put("月色山茶花", 1999); + giftMap.put("为你而来", 1688); + giftMap.put("点亮孤单", 1800); + giftMap.put("浪漫营地", 1699); + giftMap.put("薰衣草庄园", 3300); + giftMap.put("红墙白雪", 1688); + giftMap.put("华灯初上", 5000); + giftMap.put("嘉年华", 30000); + giftMap.put("单车恋人", 1899); + giftMap.put("为爱启航", 10001); + giftMap.put("镜中奇缘", 1500); + giftMap.put("仲夏夜之梦", 8999); + giftMap.put("龙珠纳福", 2388); + giftMap.put("蝶·比翼鸟", 1700); + giftMap.put("无畏守护", 10168); + giftMap.put("壁上飞仙", 4999); + giftMap.put("海上生明月", 4166); + giftMap.put("铁甲柔情", 3800); + giftMap.put("心动丘比特", 4321); + giftMap.put("变形战车", 5500); + giftMap.put("抖音飞艇", 20000); + giftMap.put("冰冻战车", 3000); + giftMap.put("星际玫瑰", 7500); + giftMap.put("奏响人生", 3666); + giftMap.put("摩天大厦", 8222); + giftMap.put("传送门", 2999); + giftMap.put("云中秘境", 13140); + giftMap.put("火龙爆发", 5000); + giftMap.put("福佑万家", 4888); + giftMap.put("天空之镜", 6399); + giftMap.put("情定三生", 9666); + giftMap.put("月下瀑布", 6666); + giftMap.put("金鳞化龙", 9000); + giftMap.put("蝶·化蝶飞", 10999); + giftMap.put("无尽浪漫", 19999); + giftMap.put("云霄大厦", 7888); + giftMap.put("梦幻城堡", 28888); + giftMap.put("真爱永恒", 8999); + giftMap.put("跨时空之恋", 9000); + giftMap.put("炫彩射击", 1888); + giftMap.put("一路有你", 17999); + giftMap.put("浪漫马车", 28888); + giftMap.put("蝶·寄相思", 6800); + giftMap.put("梦回紫禁城", 8666); + giftMap.put("小纸条", 399); + giftMap.put("如意锦囊", 99); + giftMap.put("星光瓶", 900); + giftMap.put("一直陪伴你", 520); + giftMap.put("动次打次", 2999); + giftMap.put("宇宙之心", 18888); + } + + public List getGiftNameList() { + return new ArrayList<>(giftMap.keySet()); + } + + public SqlUtil getSqlUtil() { + return sqlUtil; + } + + public SqlManager getSqlManager() { + return sqlManager; + } + + public void LinkMySqlData() { + String SQL_Host = "gz-cdb-r9koldtt.sql.tencentcdb.com"; + String SQL_Port = "29320"; + String SQL_Users = "root"; + String SQL_Password = "Pixel@123456"; + String SQL_Database = "mclivedata"; + sqlManager = new SqlManager(); + sqlUtil = new SqlUtil(SQL_Host, SQL_Port, SQL_Database, SQL_Users, SQL_Password); + sqlManager.createTable(); + } + + public int getGiftMoney(String giftName) { + if (giftMap.get(giftName) == null) { + return 1; + } + return giftMap.get(giftName); + } + + public double getTotalMoney() { + return totalMoney; + } + + public void defaultTotalMoney() { + if (this.totalMoney <= 0) { + for (Player player : Bukkit.getOnlinePlayers()) { + String name = player.getName(); + if (Main.configYml.getRoomId(name) == null) { + return; + } + String tiktok = Main.configYml.getRoomId(name); + this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; + } + } + } + + public void addTotalMoney(String name, int money) { + if (this.totalMoney <= 0) { + if (Main.configYml.getRoomId(name) == null) { + return; + } + String tiktok = Main.configYml.getRoomId(name); + this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; + } + if (money >= 5000) { + SaveZhuboData(name); + } + this.totalMoney += money; + } + + public void addTotalMoney(String name, int money, long amount) { + if (this.totalMoney <= 0) { + if (Main.configYml.getRoomId(name) == null) { + return; + } + String tiktok = Main.configYml.getRoomId(name); + this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; + } + int newMoney = (int) (money * amount); + if (newMoney >= 5000) { + SaveZhuboData(name); + } + this.totalMoney += newMoney; + } + + public void SaveZhuboData(String name) { + if (Main.configYml.getRoomId(name) == null) { + return; + } + String tiktok = Main.configYml.getRoomId(name); + if (ConfigYml.mysqlState) { + sqlManager.SavePlayerData(tiktok); + } + } + +} diff --git a/src/main/java/com/io/yutian/livemutually/manager/KSLiveRoomManager.java b/src/main/java/com/io/yutian/livemutually/manager/KSLiveRoomManager.java index 3e5c933..5bbf9a0 100644 --- a/src/main/java/com/io/yutian/livemutually/manager/KSLiveRoomManager.java +++ b/src/main/java/com/io/yutian/livemutually/manager/KSLiveRoomManager.java @@ -1,21 +1,17 @@ package com.io.yutian.livemutually.manager; -import cn.hamster3.cdapi.CDTimeAPI; -import com.io.yutian.mclive.*; import com.io.yutian.pixelliveplugin.PixelLiveAPI; -import com.io.yutian.verify.AESUtil; import org.bukkit.entity.Player; import java.util.ArrayList; import java.util.List; -import java.util.regex.Pattern; public class KSLiveRoomManager { - public static List livepluginList = new ArrayList<>(); + private static List players = new ArrayList<>(); public static boolean isConnected(Player player) { - if(livepluginList.contains(player)){ + if (players.contains(player)) { return true; } return false; @@ -25,9 +21,13 @@ public class KSLiveRoomManager { if (!isConnected(player)) { return; } - livepluginList.remove(player); + players.remove(player); PixelLiveAPI.disconnect(player); player.sendMessage("§c[系统]§b已断开直播间连接..."); } + public static List getPlayers() { + return players; + } + } diff --git a/src/main/java/com/io/yutian/livemutually/manager/RankManager.java b/src/main/java/com/io/yutian/livemutually/manager/RankManager.java new file mode 100644 index 0000000..7f33c49 --- /dev/null +++ b/src/main/java/com/io/yutian/livemutually/manager/RankManager.java @@ -0,0 +1,115 @@ +package com.io.yutian.livemutually.manager; + +import com.io.yutian.mclive.Main; +import com.io.yutian.mclive.data.UserResourceData; +import com.io.yutian.pixelengine.api.EngineAPI; +import com.io.yutian.pixelengine.api.util.ImageUtil; +import json.JSONObject; +import org.apache.commons.lang.Validate; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.entity.Player; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.stream.Collectors; + +public class RankManager { + + private static List topCache = new ArrayList<>(); + private static Map userDatas = new HashMap<>(); + + public static void addUserAmount(String userName, long score) { + if (userName == null || userName.isEmpty() || userName.length() == 0) { + return; + } + long amount = userDatas.getOrDefault(userName, score); + userDatas.put(userName, amount + score); + Bukkit.getScheduler().runTaskAsynchronously(Main.plugin, RankManager::update); + save(); + } + + public static void update() { + List tops = userDatas.entrySet().stream() + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder())) + .limit(3) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (topCache.isEmpty() || !topCache.equals(tops)) { + topCache = tops; + for (int i = 0; i < 3; i++) { + if (i >= tops.size()) { + return; + } + String name = tops.get(i); + UserResourceData userResourceData = UserManager.getUserResourceData(name); + if (userResourceData != null) { + int finalI = i; + String base64 = ImageUtil.getCacheImageBase64(userResourceData.getNickName(), userResourceData.getAvatarThumbUrl(), (newBase64) -> { + for (Player player : Bukkit.getOnlinePlayers()) { + EngineAPI.getRankAPI().setRankData(player, finalI + 1, newBase64, userResourceData.getLevel()); + } + }); + for (Player player : Bukkit.getOnlinePlayers()) { + EngineAPI.getRankAPI().setRankData(player, i + 1, base64, userResourceData.getLevel()); + } + } + } + } + } + + public static long getUserAmount(String userName) { + return userDatas.getOrDefault(userName, 0L); + } + + public static void save() { + File file = new File(Main.plugin.getDataFolder(), "rank.yml"); + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (Map.Entry entry : userDatas.entrySet()) { + String key = entry.getKey(); + if (key == null || key.isEmpty() || key.length() == 0) { + continue; + } + try { + config.set(key, entry.getValue()); + } catch (Exception e) { + } + } + try { + config.save(file); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static void load() { + File file = new File(Main.plugin.getDataFolder(), "rank.yml"); + if (!file.exists()) { + try { + file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + FileConfiguration config = YamlConfiguration.loadConfiguration(file); + for (String key : config.getKeys(false)) { + userDatas.put(key, config.getLong(key)); + } + } + + public static void clear() { + File file = new File(Main.plugin.getDataFolder(), "rank.yml"); + file.delete(); + userDatas.clear(); + } + +} diff --git a/src/main/java/com/io/yutian/livemutually/manager/UserData.java b/src/main/java/com/io/yutian/livemutually/manager/UserData.java deleted file mode 100644 index 5118082..0000000 --- a/src/main/java/com/io/yutian/livemutually/manager/UserData.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.io.yutian.livemutually.manager; - -import java.util.HashMap; - -public class UserData { - - private String userName; - // 礼物 礼物数据 - private HashMap giftData = new HashMap<>(); - - public UserData(String userName){ - this.userName = userName; - this.giftData = new HashMap<>(); - } - - public String getUserName() { - return userName; - } - - // 判断本次赠送礼物是否已送过 - public boolean isExitGift(String giftName){ - if(giftData.get(giftName) != null){ - return true; - } - return false; - } - - public GiftData getGiftsData(String giftName){ - if(giftData.get(giftName) != null){ - return giftData.get(giftName); - } - return null; - } - - public void createGiftsData(String giftName,long giftAmount){ - long sendTime = System.currentTimeMillis(); - giftData.put(giftName,new GiftData(giftName,giftAmount,sendTime)); - } -} diff --git a/src/main/java/com/io/yutian/livemutually/manager/UserManager.java b/src/main/java/com/io/yutian/livemutually/manager/UserManager.java new file mode 100644 index 0000000..2d22bfe --- /dev/null +++ b/src/main/java/com/io/yutian/livemutually/manager/UserManager.java @@ -0,0 +1,20 @@ +package com.io.yutian.livemutually.manager; + +import com.io.yutian.mclive.data.UserResourceData; + +import java.util.HashMap; +import java.util.Map; + +public class UserManager { + + private static Map userResourceDataMap = new HashMap<>(); + + public static void setUserResourceData(String userName, UserResourceData userResourceData) { + userResourceDataMap.put(userName, userResourceData); + } + + public static UserResourceData getUserResourceData(String userName) { + return userResourceDataMap.get(userName); + } + +} diff --git a/src/main/java/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.java b/src/main/java/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.java index f25d721..6a678d9 100644 --- a/src/main/java/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.java +++ b/src/main/java/com/io/yutian/livemutually/wss/KSAPILiveRoomWatcher.java @@ -15,19 +15,19 @@ public class KSAPILiveRoomWatcher extends LiveRoomWatcher { public KSAPILiveRoomWatcher(Player player) { this.player = player; onChat(chat -> { - Bukkit.getScheduler().runTask(Main.plugin, ()-> { + Bukkit.getScheduler().runTask(Main.plugin, () -> { LiveChatEvents event = new LiveChatEvents(player, chat.user(), chat.content()); Bukkit.getPluginManager().callEvent(event); }); }); onUser(user -> { - Bukkit.getScheduler().runTask(Main.plugin, ()-> { + Bukkit.getScheduler().runTask(Main.plugin, () -> { LiveEnterEvents event = new LiveEnterEvents(player, user); Bukkit.getPluginManager().callEvent(event); }); }); onLike(like -> { - Bukkit.getScheduler().runTask(Main.plugin, ()-> { + Bukkit.getScheduler().runTask(Main.plugin, () -> { LiveLikeEvents event = new LiveLikeEvents(player, like.user(), like.count()); Bukkit.getPluginManager().callEvent(event); }); diff --git a/src/main/java/com/io/yutian/mclive/Main.java b/src/main/java/com/io/yutian/mclive/Main.java index c27d5c4..3d9b872 100644 --- a/src/main/java/com/io/yutian/mclive/Main.java +++ b/src/main/java/com/io/yutian/mclive/Main.java @@ -1,7 +1,9 @@ package com.io.yutian.mclive; import com.io.yutian.livemutually.manager.KSLiveRoomManager; -import com.io.yutian.mclive.data.GiftManage; +import com.io.yutian.livemutually.manager.GiftManager; +import com.io.yutian.livemutually.manager.RankManager; +import com.io.yutian.livemutually.manager.UserManager; import com.io.yutian.mclive.event.ZhuboAPI; import com.io.yutian.mclive.listener.JoinGameRoom; import com.io.yutian.mclive.listener.LiveAdminGui; @@ -9,13 +11,14 @@ import com.io.yutian.mclive.listener.SoundsMenu; import com.io.yutian.mclive.live.LiveEvent; import com.io.yutian.mclive.live.ModEvent; import com.io.yutian.mclive.util.ConfigYml; +import com.io.yutian.pixelliveplugin.PixelLiveAPI; import org.bukkit.Bukkit; import org.bukkit.Sound; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.entity.*; +import org.bukkit.entity.Player; import org.bukkit.plugin.Plugin; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitRunnable; @@ -30,45 +33,10 @@ public class Main extends JavaPlugin { public static Main plugin; public static boolean check_plugin = false; public static ConfigYml configYml; - public static GiftManage giftManage; + public static GiftManager giftManager; public static boolean giftStatistics = true; - @Override - public void onEnable() { - plugin = this; - SendPluginsAuthorMessage(Bukkit.getConsoleSender()); - configYml = new ConfigYml(getConfig()); - if (giftStatistics){ - giftManage = new GiftManage(); - giftManage.LinkMySqlData(); - } - addSoundKeyList(); - getServer().getPluginManager().registerEvents(new LiveAdminGui(),this); - getServer().getPluginManager().registerEvents(new JoinGameRoom(),this); - getServer().getPluginManager().registerEvents(new ModEvent(),this); - getServer().getPluginManager().registerEvents(new LiveEvent(configYml),this); - getServer().getPluginManager().registerEvents(new SoundsMenu(),this); - Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7当前版本: §ev"+plugin.getDescription().getVersion()); - Plugin plugin = getServer().getPluginManager().getPlugin(configYml.getGameMode()); - if (plugin != null) { - check_plugin = true; - Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §a已激活游戏模式"); - if(giftStatistics) { - Bukkit.getScheduler().runTaskTimerAsynchronously(this, new Runnable() { - @Override - public void run() { - for (Player player : Bukkit.getOnlinePlayers()) { - giftManage.SaveZhuboData(player.getName()); - } - } - }, 0L, 24000L); - } - } else { - Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §c未检测到§a<"+configYml.getGameMode()+">§c游戏插件."); - } - } - - public static void addSoundKeyList(){ + public static void addSoundKeyList() { List stringList = new ArrayList<>(); stringList.add("youbom"); stringList.add("oi"); @@ -121,11 +89,91 @@ public class Main extends JavaPlugin { SoundsMenu.soundList.addAll(stringList); } + public static void SendPluginsAuthorMessage(CommandSender sender) { + sender.sendMessage(" "); + sender.sendMessage("§b ██ ████"); + sender.sendMessage("§b ██"); + sender.sendMessage("§b██░███▒ ████ ███ ███ ░████▒ ██"); + sender.sendMessage("§b███████▒ ████ ██▒▒██ ░██████▒ ██"); + sender.sendMessage("§b███ ███ ██ ▒████▒ ██▒ ▒██ ██"); + sender.sendMessage("§b██░ ░██ ██ ████ ████████ ██"); + sender.sendMessage("§b██ ██ ██ ▒██▒ ████████ ██"); + sender.sendMessage("§b██░ ░██ ██ ████ ██ ██"); + sender.sendMessage("§b███ ███ ██ ▒████▒ ███░ ▒█ ██▒"); + sender.sendMessage("§b███████▒ ████████ ██▒▒██ ░███████ █████"); + sender.sendMessage("§b██░███▒ ████████ ███ ███ ░█████▒ ░████"); + sender.sendMessage("§b██"); + sender.sendMessage("§b█ 弹幕互动引擎 由极光像素工作室提供技术支持!!"); + sender.sendMessage("§b█ 如有使用问题可联系售后: §d" + getAuthorFile().getString("author")); + sender.sendMessage("" + getAuthorFile().getString("linemessage")); + } + + public static FileConfiguration getAuthorFile() { + File ShopMenufile = new File("plugins/PluginMetrics", "config.yml"); + return YamlConfiguration.loadConfiguration(ShopMenufile); + } + + public static String HideName(String audience) { + if (audience.length() <= 2) { + return "**"; + } + // 获取第一个和第二个字符 + char firstChar = audience.charAt(0); + char lastChar = audience.charAt(audience.length() - 1); + // 构建屏蔽后的字符串 + StringBuilder maskedString = new StringBuilder(); + for (int i = 1; i < audience.length() - 1; i++) { + maskedString.append('*'); + } + return String.valueOf(firstChar) + maskedString + lastChar; + } + + public static String getMinecraftVersion() { + String packageName = Bukkit.getServer().getClass().getPackage().getName(); + String version = packageName.substring(packageName.lastIndexOf('.') + 1); + return version; + } + + @Override + public void onEnable() { + plugin = this; + SendPluginsAuthorMessage(Bukkit.getConsoleSender()); + configYml = new ConfigYml(getConfig()); + if (giftStatistics) { + giftManager = new GiftManager(); + giftManager.LinkMySqlData(); + } + addSoundKeyList(); + getServer().getPluginManager().registerEvents(new LiveAdminGui(), this); + getServer().getPluginManager().registerEvents(new JoinGameRoom(), this); + getServer().getPluginManager().registerEvents(new ModEvent(), this); + getServer().getPluginManager().registerEvents(new LiveEvent(configYml), this); + getServer().getPluginManager().registerEvents(new SoundsMenu(), this); + Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7当前版本: §ev" + plugin.getDescription().getVersion()); + Plugin plugin = getServer().getPluginManager().getPlugin(configYml.getGameMode()); + check_plugin = true; + if (plugin != null) { + RankManager.load(); + check_plugin = true; + Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §a已激活游戏模式"); + if (giftStatistics) { + Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> { + for (Player player : Bukkit.getOnlinePlayers()) { + giftManager.SaveZhuboData(player.getName()); + } + }, 0L, 24000L); + } + } else { + Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §c未检测到§a<" + configYml.getGameMode() + ">§c游戏插件."); + } + } + + @Override public void onDisable() { - if(giftStatistics && ConfigYml.mysqlState) { + if (giftStatistics && ConfigYml.mysqlState) { for (Player player : Bukkit.getOnlinePlayers()) { - giftManage.SaveZhuboData(player.getName()); + giftManager.SaveZhuboData(player.getName()); } } } @@ -133,7 +181,7 @@ public class Main extends JavaPlugin { @Override public boolean onCommand(CommandSender sender, Command command, String Command, String[] args) { if (Command.equalsIgnoreCase("sounds")) { - SoundsMenu.OpenGui((Player) sender,1); + SoundsMenu.OpenGui((Player) sender, 1); } if (Command.equalsIgnoreCase("mclive")) { if (args.length == 0) { @@ -151,14 +199,20 @@ public class Main extends JavaPlugin { sender.sendMessage(""); return true; } + if (args.length == 1 && args[0].equalsIgnoreCase("test")) { + Player player = (Player) sender; + + PixelLiveAPI.connect(player, "127.0.0.1", 9018); + return true; + } if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { Main.configYml.ReloadConfig(); sender.sendMessage("§c[系统]§a配置文件已重载"); return true; } if (args.length == 1 && args[0].equalsIgnoreCase("save")) { - for (Player player : Bukkit.getOnlinePlayers()){ - giftManage.SaveZhuboData(player.getName()); + for (Player player : Bukkit.getOnlinePlayers()) { + giftManager.SaveZhuboData(player.getName()); } sender.sendMessage("§c[系统]§a数据已提交至云数据库."); return true; @@ -177,7 +231,7 @@ public class Main extends JavaPlugin { } if (args.length == 1 && args[0].equalsIgnoreCase("stop")) { ZhuboAPI.wsRoomIdSame.clear(); - for (Player player : KSLiveRoomManager.livepluginList) { + for (Player player : KSLiveRoomManager.getPlayers()) { KSLiveRoomManager.disconnect(player); } Bukkit.broadcastMessage("§c[系统]§a已断开所有主播的连接."); @@ -204,14 +258,14 @@ public class Main extends JavaPlugin { Main.configYml.SaveConfigYml(); sender.sendMessage("§c[系统]§a直播间已设置 §e" + playName + " §a--> §e" + room_id); player.resetTitle(); - player.sendTitle("§a§l绑定成功!","§6已绑定抖音号: §f"+room_id,0,40,20); - player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP,1,1); + player.sendTitle("§a§l绑定成功!", "§6已绑定抖音号: §f" + room_id, 0, 40, 20); + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1); new BukkitRunnable() { @Override public void run() { - player.sendTitle("§c尚未连接弹幕软件","§6按住SHIFT键 + F键 §f点击按钮连接",5,20*60,20); + player.sendTitle("§c尚未连接弹幕软件", "§6按住SHIFT键 + F键 §f点击按钮连接", 5, 20 * 60, 20); } - }.runTaskLater(this,40); + }.runTaskLater(this, 40); return true; } if (args.length == 1 && args[0].equalsIgnoreCase("save")) { @@ -230,49 +284,4 @@ public class Main extends JavaPlugin { } return false; } - - public static void SendPluginsAuthorMessage(CommandSender sender) { - sender.sendMessage(" "); - sender.sendMessage("§b ██ ████"); - sender.sendMessage("§b ██"); - sender.sendMessage("§b██░███▒ ████ ███ ███ ░████▒ ██"); - sender.sendMessage("§b███████▒ ████ ██▒▒██ ░██████▒ ██"); - sender.sendMessage("§b███ ███ ██ ▒████▒ ██▒ ▒██ ██"); - sender.sendMessage("§b██░ ░██ ██ ████ ████████ ██"); - sender.sendMessage("§b██ ██ ██ ▒██▒ ████████ ██"); - sender.sendMessage("§b██░ ░██ ██ ████ ██ ██"); - sender.sendMessage("§b███ ███ ██ ▒████▒ ███░ ▒█ ██▒"); - sender.sendMessage("§b███████▒ ████████ ██▒▒██ ░███████ █████"); - sender.sendMessage("§b██░███▒ ████████ ███ ███ ░█████▒ ░████"); - sender.sendMessage("§b██"); - sender.sendMessage("§b█ 弹幕互动引擎 由极光像素工作室提供技术支持!!"); - sender.sendMessage("§b█ 如有使用问题可联系售后: §d"+getAuthorFile().getString("author")); - sender.sendMessage(""+getAuthorFile().getString("linemessage")); - } - - public static FileConfiguration getAuthorFile(){ - File ShopMenufile = new File("plugins/PluginMetrics", "config.yml"); - return YamlConfiguration.loadConfiguration(ShopMenufile); - } - - public static String HideName(String audience){ - if(audience.length() <= 2){ - return "**"; - } - // 获取第一个和第二个字符 - char firstChar = audience.charAt(0); - char lastChar = audience.charAt(audience.length() - 1); - // 构建屏蔽后的字符串 - StringBuilder maskedString = new StringBuilder(); - for (int i = 1; i < audience.length() - 1; i++) { - maskedString.append('*'); - } - return String.valueOf(firstChar) + maskedString + lastChar; - } - - public static String getMinecraftVersion() { - String packageName = Bukkit.getServer().getClass().getPackage().getName(); - String version = packageName.substring(packageName.lastIndexOf('.') + 1); - return version; - } } diff --git a/src/main/java/com/io/yutian/livemutually/manager/GiftData.java b/src/main/java/com/io/yutian/mclive/data/GiftData.java similarity index 81% rename from src/main/java/com/io/yutian/livemutually/manager/GiftData.java rename to src/main/java/com/io/yutian/mclive/data/GiftData.java index adcb49d..6b4d007 100644 --- a/src/main/java/com/io/yutian/livemutually/manager/GiftData.java +++ b/src/main/java/com/io/yutian/mclive/data/GiftData.java @@ -1,4 +1,4 @@ -package com.io.yutian.livemutually.manager; +package com.io.yutian.mclive.data; public class GiftData { @@ -8,7 +8,7 @@ public class GiftData { private long sendTime; - public GiftData(String name,long amount,long sendTime){ + public GiftData(String name, long amount, long sendTime) { this.name = name; this.amount = amount; this.sendTime = sendTime; diff --git a/src/main/java/com/io/yutian/mclive/data/GiftManage.java b/src/main/java/com/io/yutian/mclive/data/GiftManage.java deleted file mode 100644 index 8fd0de3..0000000 --- a/src/main/java/com/io/yutian/mclive/data/GiftManage.java +++ /dev/null @@ -1,243 +0,0 @@ -package com.io.yutian.mclive.data; - -import com.io.yutian.mclive.util.ConfigYml; -import com.io.yutian.mclive.Main; -import com.io.yutian.mclive.util.SqlUtil; -import com.io.yutian.mclive.data.database.SqlManager; -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; - -public class GiftManage { - - private double totalMoney; - private LinkedHashMap giftMap = new LinkedHashMap<>(); - - public List getGiftNameList() { - return new ArrayList<>(giftMap.keySet()); - } - - public GiftManage(){ - totalMoney = 0; - giftMap.put("小心心",1); - giftMap.put("人气票",1); - giftMap.put("闪耀星辰",1); - giftMap.put("加油鸭",1); - giftMap.put("比心兔兔",1); - giftMap.put("热气球",1); - giftMap.put("爱你哟",1); - giftMap.put("天鹅之梦",1); - giftMap.put("Thuglife",1); - giftMap.put("粘人小狗",1); - giftMap.put("粉丝团灯牌",1); - giftMap.put("大啤酒",2); - giftMap.put("玫瑰",1); - giftMap.put("抖音",1); - giftMap.put("称心如意",1); - giftMap.put("你最好看",2); - giftMap.put("助力票",1); - giftMap.put("荣耀擂鼓",99); - giftMap.put("宠粉季",1); - giftMap.put("兔耳朵",99); - giftMap.put("游戏手柄",99); - giftMap.put("冰镇西瓜",99); - giftMap.put("为你闪耀",9); - giftMap.put("棒棒糖",9); - giftMap.put("鲜花",10); - giftMap.put("亲吻",99); - giftMap.put("跑车",1200); - giftMap.put("礼花筒",199); - giftMap.put("捏捏小脸",99); - giftMap.put("鹿仙子",99); - giftMap.put("夏威夷花环",99); - giftMap.put("爱的纸鹤",99); - giftMap.put("送你花花",49); - giftMap.put("女神花环",99); - giftMap.put("鱼你一起",99); - giftMap.put("真爱玫瑰",366); - giftMap.put("为你举牌",199); - giftMap.put("龙抬头",99); - giftMap.put("花开烂漫",466); - giftMap.put("比心",199); - giftMap.put("真的爱你",520); - giftMap.put("万象烟花",688); - giftMap.put("私人飞机",3000); - giftMap.put("浪漫烟花",599); - giftMap.put("闪亮登场",460); - giftMap.put("多喝热水",126); - giftMap.put("一点心意",266); - giftMap.put("荧光棒",99); - giftMap.put("娶你回家",599); - giftMap.put("掌上明珠",888); - giftMap.put("摧残舞台",899); - giftMap.put("星星点灯",268); - giftMap.put("一束花开",366); - giftMap.put("小傻猪",299); - giftMap.put("环球旅行车",650); - giftMap.put("爱的守护",299); - giftMap.put("好运莲莲鸭",299); - giftMap.put("日出相伴",726); - giftMap.put("永生花",520); - giftMap.put("纸短情长",921); - giftMap.put("直升机",2999); - giftMap.put("蝶·连理枝",280); - giftMap.put("爱情树下",599); - giftMap.put("灵龙现世",600); - giftMap.put("爱心煎蛋",99); - giftMap.put("夏日回忆",1000); - giftMap.put("抖音1号",10001); - giftMap.put("繁花秘语",1314); - giftMap.put("ONE礼挑一",299); - giftMap.put("重拳出击",199); - giftMap.put("花落长亭",1588); - giftMap.put("浪漫恋人",1999); - giftMap.put("花海泛舟",2800); - giftMap.put("豪华邮轮",6000); - giftMap.put("环游世界",3000); - giftMap.put("蝶·书中情",750); - giftMap.put("带你去海边",4500); - giftMap.put("蜜蜂叮叮",1000); - giftMap.put("奇幻八音盒",2399); - giftMap.put("光之祝福",1999); - giftMap.put("消暑罐头车",1500); - giftMap.put("月色山茶花",1999); - giftMap.put("为你而来",1688); - giftMap.put("点亮孤单",1800); - giftMap.put("浪漫营地",1699); - giftMap.put("薰衣草庄园",3300); - giftMap.put("红墙白雪",1688); - giftMap.put("华灯初上",5000); - giftMap.put("嘉年华",30000); - giftMap.put("单车恋人",1899); - giftMap.put("为爱启航",10001); - giftMap.put("镜中奇缘",1500); - giftMap.put("仲夏夜之梦",8999); - giftMap.put("龙珠纳福",2388); - giftMap.put("蝶·比翼鸟",1700); - giftMap.put("无畏守护",10168); - giftMap.put("壁上飞仙",4999); - giftMap.put("海上生明月",4166); - giftMap.put("铁甲柔情",3800); - giftMap.put("心动丘比特",4321); - giftMap.put("变形战车",5500); - giftMap.put("抖音飞艇",20000); - giftMap.put("冰冻战车",3000); - giftMap.put("星际玫瑰",7500); - giftMap.put("奏响人生",3666); - giftMap.put("摩天大厦",8222); - giftMap.put("传送门",2999); - giftMap.put("云中秘境",13140); - giftMap.put("火龙爆发",5000); - giftMap.put("福佑万家",4888); - giftMap.put("天空之镜",6399); - giftMap.put("情定三生",9666); - giftMap.put("月下瀑布",6666); - giftMap.put("金鳞化龙",9000); - giftMap.put("蝶·化蝶飞",10999); - giftMap.put("无尽浪漫",19999); - giftMap.put("云霄大厦",7888); - giftMap.put("梦幻城堡",28888); - giftMap.put("真爱永恒",8999); - giftMap.put("跨时空之恋",9000); - giftMap.put("炫彩射击",1888); - giftMap.put("一路有你",17999); - giftMap.put("浪漫马车",28888); - giftMap.put("蝶·寄相思",6800); - giftMap.put("梦回紫禁城",8666); - giftMap.put("小纸条",399); - giftMap.put("如意锦囊",99); - giftMap.put("星光瓶",900); - giftMap.put("一直陪伴你",520); - giftMap.put("动次打次",2999); - giftMap.put("宇宙之心",18888); - } - - private SqlUtil sqlUtil; - private SqlManager sqlManager; - - public SqlUtil getSqlUtil() { - return sqlUtil; - } - - public SqlManager getSqlManager() { - return sqlManager; - } - - public void LinkMySqlData(){ - String SQL_Host = "gz-cdb-r9koldtt.sql.tencentcdb.com"; - String SQL_Port = "29320"; - String SQL_Users = "root"; - String SQL_Password = "Pixel@123456"; - String SQL_Database = "mclivedata"; - sqlManager = new SqlManager(); - sqlUtil = new SqlUtil(SQL_Host,SQL_Port,SQL_Database,SQL_Users,SQL_Password); - sqlManager.createTable(); - } - - public int getGiftMoney(String giftName){ - if(giftMap.get(giftName) == null){ - return 1; - } - return giftMap.get(giftName); - } - - public double getTotalMoney() { - return totalMoney; - } - - public void defaultTotalMoney(){ - if(this.totalMoney <= 0) { - for (Player player : Bukkit.getOnlinePlayers()) { - String name = player.getName(); - if (Main.configYml.getRoomId(name) == null) { - return; - } - String tiktok = Main.configYml.getRoomId(name); - this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; - } - } - } - public void addTotalMoney(String name,int money){ - if(this.totalMoney <= 0){ - if(Main.configYml.getRoomId(name) == null){ - return; - } - String tiktok = Main.configYml.getRoomId(name); - this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; - } - if(money >= 5000){ - SaveZhuboData(name); - } - this.totalMoney += money; - } - - public void addTotalMoney(String name,int money, long amount){ - if(this.totalMoney <= 0){ - if(Main.configYml.getRoomId(name) == null){ - return; - } - String tiktok = Main.configYml.getRoomId(name); - this.totalMoney = sqlManager.getZhuboIncome(tiktok) * 10; - } - int newMoney = (int) (money * amount); - if(newMoney >= 5000){ - SaveZhuboData(name); - } - this.totalMoney += newMoney; - } - - public void SaveZhuboData(String name) { - if (Main.configYml.getRoomId(name) == null) { - return; - } - String tiktok = Main.configYml.getRoomId(name); - if (ConfigYml.mysqlState) { - sqlManager.SavePlayerData(tiktok); - } - } - -} diff --git a/src/main/java/com/io/yutian/mclive/data/IPGeolocation.java b/src/main/java/com/io/yutian/mclive/data/IPGeolocation.java index a704ba8..8f79502 100644 --- a/src/main/java/com/io/yutian/mclive/data/IPGeolocation.java +++ b/src/main/java/com/io/yutian/mclive/data/IPGeolocation.java @@ -10,7 +10,7 @@ import java.net.URL; public class IPGeolocation { - public static String getIpAddress(){ + public static String getIpAddress() { String apiUrl = "https://api.ipify.org"; try { URL url = new URL(apiUrl); @@ -27,9 +27,9 @@ public class IPGeolocation { return "127.0.0.1"; } - public static String getThePlayerSPhysicalAddress(){ + public static String getThePlayerSPhysicalAddress() { String ipAddress = getIpAddress(); - if(!ipAddress.equalsIgnoreCase("127.0.0.1")) { + if (!ipAddress.equalsIgnoreCase("127.0.0.1")) { String apiKey = "7ccd51489d506caed949fc5ef79bf532"; // 替换成你从API服务提供商获取的API密钥 String apiUrl = "https://api.ip138.com/ipdata/?ip=" + ipAddress + "&datatype=jsonp&token=" + apiKey; try { diff --git a/src/main/java/com/io/yutian/mclive/data/UserData.java b/src/main/java/com/io/yutian/mclive/data/UserData.java new file mode 100644 index 0000000..574d0ba --- /dev/null +++ b/src/main/java/com/io/yutian/mclive/data/UserData.java @@ -0,0 +1,39 @@ +package com.io.yutian.mclive.data; + +import java.util.HashMap; + +public class UserData { + + private String userName; + // 礼物 礼物数据 + private HashMap giftData = new HashMap<>(); + + public UserData(String userName) { + this.userName = userName; + this.giftData = new HashMap<>(); + } + + public String getUserName() { + return userName; + } + + // 判断本次赠送礼物是否已送过 + public boolean isExitGift(String giftName) { + if (giftData.get(giftName) != null) { + return true; + } + return false; + } + + public GiftData getGiftsData(String giftName) { + if (giftData.get(giftName) != null) { + return giftData.get(giftName); + } + return null; + } + + public void createGiftsData(String giftName, long giftAmount) { + long sendTime = System.currentTimeMillis(); + giftData.put(giftName, new GiftData(giftName, giftAmount, sendTime)); + } +} diff --git a/src/main/java/com/io/yutian/mclive/data/UserResourceData.java b/src/main/java/com/io/yutian/mclive/data/UserResourceData.java new file mode 100644 index 0000000..9854c67 --- /dev/null +++ b/src/main/java/com/io/yutian/mclive/data/UserResourceData.java @@ -0,0 +1,29 @@ +package com.io.yutian.mclive.data; + +import java.io.File; + +public class UserResourceData { + + private final String nickName; + private String avatarThumbUrl; + private int level; + + public UserResourceData(String nickName, String avatarThumbUrl, int level) { + this.nickName = nickName; + this.avatarThumbUrl = avatarThumbUrl; + this.level = level; + } + + public String getNickName() { + return nickName; + } + + public String getAvatarThumbUrl() { + return avatarThumbUrl; + } + + public int getLevel() { + return level; + } + +} diff --git a/src/main/java/com/io/yutian/mclive/data/database/SqlManager.java b/src/main/java/com/io/yutian/mclive/data/database/SqlManager.java index 7395e1d..83edfeb 100644 --- a/src/main/java/com/io/yutian/mclive/data/database/SqlManager.java +++ b/src/main/java/com/io/yutian/mclive/data/database/SqlManager.java @@ -1,9 +1,9 @@ package com.io.yutian.mclive.data.database; import com.io.yutian.mclive.Main; +import com.io.yutian.livemutually.manager.GiftManager; import com.io.yutian.mclive.data.IPGeolocation; import com.io.yutian.mclive.util.SqlUtil; -import com.io.yutian.mclive.data.GiftManage; import java.sql.ResultSet; import java.time.Instant; @@ -15,13 +15,13 @@ import java.util.HashMap; public class SqlManager { - public String table = "income_"+Main.configYml.getGameMode().toLowerCase(); + public String table = "income_" + Main.configYml.getGameMode().toLowerCase(); // 创建数据库表格格式 public void createTable() { // 数据库结构组成 // 主播名(VARCHAR) 抖音号(VARCHAR) 直播流水(VARCHAR) 记录时间(VARCHAR) 创建时间(VARCHAR) - String s = "CREATE TABLE IF NOT EXISTS "+table+"(" + + String s = "CREATE TABLE IF NOT EXISTS " + table + "(" + " zhubo VARCHAR(32) NOT NULL," + " tiktok VARCHAR(32) NOT NULL," + " income DOUBLE NOT NULL," + @@ -35,10 +35,10 @@ public class SqlManager { } // 创建主播档案数据 - public void createAnchorProfile(String tiktok){ + public void createAnchorProfile(String tiktok) { getSQL().openConnection(); int iconme = -1; - String select = "SELECT * FROM "+table+" WHERE tiktok = '%tiktok%'"; + String select = "SELECT * FROM " + table + " WHERE tiktok = '%tiktok%'"; try { ResultSet resultSet = getSQL().querySQL(select.replace("%tiktok%", tiktok)); while (resultSet.next()) { @@ -47,7 +47,7 @@ public class SqlManager { } catch (Exception e) { e.printStackTrace(); } - if(iconme <= -1) { + if (iconme <= -1) { String set = "INSERT INTO " + table + " (`zhubo`,`tiktok`, `income`, `livetime`, `createtime`, `city`) " + "VALUES ('%zhubo%','%tiktok%', '%income%', '%livetime%', '%createtime%', '%city%')"; set = set.replace("%zhubo%", "默认"); @@ -61,7 +61,7 @@ public class SqlManager { getSQL().closeConnection(); } - public String getNowTimeString(){ + public String getNowTimeString() { // 获取当前时间戳 Instant now = Instant.now(); // 将时间戳转换为本地日期时间 @@ -72,13 +72,13 @@ public class SqlManager { } public double getZhuboIncome(String tiktok) { - String select = "SELECT * FROM "+table+" WHERE tiktok = '%tiktok%'"; + String select = "SELECT * FROM " + table + " WHERE tiktok = '%tiktok%'"; try { getSQL().openConnection(); ResultSet resultSet = getSQL().querySQL(select.replace("%tiktok%", tiktok)); while (resultSet.next()) { double income = resultSet.getDouble("income"); - // System.out.println("[调试 - 输出] "+tiktok+" 数据: "+income+"元"); + // System.out.println("[调试 - 输出] "+tiktok+" 数据: "+income+"元"); return income; } } catch (Exception e) { @@ -91,7 +91,7 @@ public class SqlManager { public HashMap getAllZhuboData() { HashMap map = new HashMap<>(); - String select = "SELECT * FROM "+table; + String select = "SELECT * FROM " + table; try { getSQL().openConnection(); ResultSet set = getSQL().querySQL(select); @@ -108,24 +108,26 @@ public class SqlManager { return map; } - public void SavePlayerData(String tiktok){ - if(!Main.giftStatistics){return;} - GiftManage giftManage = Main.giftManage; - giftManage.defaultTotalMoney(); - giftManage.addTotalMoney(tiktok,1); - double money = giftManage.getTotalMoney() * 0.1; - String set = "UPDATE `"+table+"` SET " + + public void SavePlayerData(String tiktok) { + if (!Main.giftStatistics) { + return; + } + GiftManager giftManager = Main.giftManager; + giftManager.defaultTotalMoney(); + giftManager.addTotalMoney(tiktok, 1); + double money = giftManager.getTotalMoney() * 0.1; + String set = "UPDATE `" + table + "` SET " + "`income` = '%income%'," + - "`livetime` = '%livetime%' WHERE `"+table+"`.`tiktok` = '%tiktok%'"; + "`livetime` = '%livetime%' WHERE `" + table + "`.`tiktok` = '%tiktok%'"; getSQL().openConnection(); getSQL().updateSQL(set.replace("%tiktok%", tiktok). - replace("%income%", String.format("%.1f",money)). - replace("%livetime%",getNowTimeString())); + replace("%income%", String.format("%.1f", money)). + replace("%livetime%", getNowTimeString())); getSQL().closeConnection(); } private SqlUtil getSQL() { - GiftManage giftManage = Main.giftManage; - return giftManage.getSqlUtil(); + GiftManager giftManager = Main.giftManager; + return giftManager.getSqlUtil(); } } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveChatEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveChatEvents.java index c99b6be..1832b73 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveChatEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveChatEvents.java @@ -20,6 +20,10 @@ public class LiveChatEvents extends Event { this.content = content; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -37,8 +41,4 @@ public class LiveChatEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveConnectEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveConnectEvents.java index d7bdda5..83240a2 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveConnectEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveConnectEvents.java @@ -1,6 +1,5 @@ package com.io.yutian.mclive.event; -import com.io.yutian.livemutually.liveroom.User; import org.bukkit.entity.Player; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -11,12 +10,16 @@ public class LiveConnectEvents extends Event { private String roomID; private String pluginName; - public LiveConnectEvents(Player player, String roomId,String pluginName) { + public LiveConnectEvents(Player player, String roomId, String pluginName) { this.player = player; this.roomID = roomId; this.pluginName = pluginName; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -34,8 +37,4 @@ public class LiveConnectEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveEnterEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveEnterEvents.java index a257e0d..658382b 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveEnterEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveEnterEvents.java @@ -18,6 +18,10 @@ public class LiveEnterEvents extends Event { this.user = user; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -31,8 +35,4 @@ public class LiveEnterEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveFollowEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveFollowEvents.java index bf0cc65..6fd9363 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveFollowEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveFollowEvents.java @@ -17,6 +17,10 @@ public class LiveFollowEvents extends Event { this.user = user; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -30,8 +34,4 @@ public class LiveFollowEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveGiftEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveGiftEvents.java index 2d29216..1dfb9fa 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveGiftEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveGiftEvents.java @@ -22,6 +22,10 @@ public class LiveGiftEvents extends Event { this.amount = amount; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -43,8 +47,4 @@ public class LiveGiftEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/LiveLikeEvents.java b/src/main/java/com/io/yutian/mclive/event/LiveLikeEvents.java index 93da9d2..9319e9d 100644 --- a/src/main/java/com/io/yutian/mclive/event/LiveLikeEvents.java +++ b/src/main/java/com/io/yutian/mclive/event/LiveLikeEvents.java @@ -19,6 +19,10 @@ public class LiveLikeEvents extends Event { this.count = count; } + public final static HandlerList getHandlerList() { + return handlers; + } + public Player getPlayer() { return player; } @@ -36,8 +40,4 @@ public class LiveLikeEvents extends Event { return handlers; } - public final static HandlerList getHandlerList() { - return handlers; - } - } diff --git a/src/main/java/com/io/yutian/mclive/event/ZhuboAPI.java b/src/main/java/com/io/yutian/mclive/event/ZhuboAPI.java index e1dd452..44faa1c 100644 --- a/src/main/java/com/io/yutian/mclive/event/ZhuboAPI.java +++ b/src/main/java/com/io/yutian/mclive/event/ZhuboAPI.java @@ -2,32 +2,35 @@ package com.io.yutian.mclive.event; import com.io.yutian.livemutually.manager.KSLiveRoomManager; import com.io.yutian.mclive.Main; -import org.bukkit.Bukkit; import org.bukkit.entity.Player; import java.util.HashMap; -import java.util.List; public abstract class ZhuboAPI { // 存储Wss主播的直播间id - public static HashMap wsRoomIdSame = new HashMap<>(); - public static boolean isWsRoomIdSame(Player player){ - if(wsRoomIdSame.get(player) == null){ + public static HashMap wsRoomIdSame = new HashMap<>(); + + public static boolean isWsRoomIdSame(Player player) { + if (wsRoomIdSame.get(player) == null) { return false; } return wsRoomIdSame.get(player); } + // 获取主播的连接状态 - public static boolean isRoomisConnected(Player zhubo){ + public static boolean isRoomisConnected(Player zhubo) { return KSLiveRoomManager.isConnected(zhubo); } - public static String getRoomLiveName(){return "DouYin";} + + public static String getRoomLiveName() { + return "DouYin"; + } //public static String getRoomLiveName(){return "KuaiShou";} // 获取礼物列表 - public static boolean isGiftNameList(String giftname){ - for (String gift : Main.giftManage.getGiftNameList()){ - if(giftname.contains(gift)){ + public static boolean isGiftNameList(String giftname) { + for (String gift : Main.giftManager.getGiftNameList()) { + if (giftname.contains(gift)) { return true; } } diff --git a/src/main/java/com/io/yutian/mclive/listener/JoinGameRoom.java b/src/main/java/com/io/yutian/mclive/listener/JoinGameRoom.java index a8fbe53..cb50829 100644 --- a/src/main/java/com/io/yutian/mclive/listener/JoinGameRoom.java +++ b/src/main/java/com/io/yutian/mclive/listener/JoinGameRoom.java @@ -16,6 +16,13 @@ import org.bukkit.event.player.PlayerSwapHandItemsEvent; public class JoinGameRoom implements Listener { + public static void SendClickMessage(Player player, String message, String roomId) { + TextComponent tomessage = new TextComponent(message); + tomessage.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://live.douyin.com/" + roomId)); + tomessage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§9By.极光像素工作室").create())); + player.spigot().sendMessage(tomessage); + } + @EventHandler public void onSwap(PlayerSwapHandItemsEvent e) { Player p = e.getPlayer(); @@ -31,7 +38,7 @@ public class JoinGameRoom implements Listener { } @EventHandler - public void onJoin(PlayerJoinEvent e){ + public void onJoin(PlayerJoinEvent e) { Player p = e.getPlayer(); e.setJoinMessage(null); if (!Main.check_plugin) { @@ -39,35 +46,28 @@ public class JoinGameRoom implements Listener { } String playName = p.getName(); String roomId = Main.configYml.getRoomId(playName); - if(Main.configYml.getRoomId(playName) != null) { + if (Main.configYml.getRoomId(playName) != null) { String message = "§7[§6提示§7] §f当前账号: §a§n" + playName + "§r §f抖音号: §b" + roomId + " §c【点击复制链接】"; SendClickMessage(p, message, roomId); SendClickMessage(p, message, roomId); SendClickMessage(p, message, roomId); - p.sendTitle("§c尚未连接弹幕软件","§6按住SHIFT键 + F键 §f点击按钮连接",0,20*60,20); - }else{ + p.sendTitle("§c尚未连接弹幕软件", "§6按住SHIFT键 + F键 §f点击按钮连接", 0, 20 * 60, 20); + } else { String message = "§7[§6提示§7] §f当前账号: §c§n" + playName + "§r §f并未绑定抖音ID"; p.sendMessage(message); - p.sendTitle("§c尚未绑定抖音号","§6输入指令: §f/mclive 抖音号",0,20 * 60,20); + p.sendTitle("§c尚未绑定抖音号", "§6输入指令: §f/mclive 抖音号", 0, 20 * 60, 20); } } - public static void SendClickMessage(Player player,String message,String roomId){ - TextComponent tomessage = new TextComponent(message); - tomessage.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, "https://live.douyin.com/"+roomId)); - tomessage.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§9By.极光像素工作室").create())); - player.spigot().sendMessage(tomessage); - } - @EventHandler - public void onQuit(PlayerQuitEvent e){ + public void onQuit(PlayerQuitEvent e) { Player player = e.getPlayer(); String playName = player.getName(); Main.configYml.SaveConfigYml(); } @EventHandler - public void onKick(PlayerKickEvent e){ + public void onKick(PlayerKickEvent e) { Player player = e.getPlayer(); String playName = player.getName(); Main.configYml.SaveConfigYml(); diff --git a/src/main/java/com/io/yutian/mclive/listener/LiveAdminGui.java b/src/main/java/com/io/yutian/mclive/listener/LiveAdminGui.java index 91940cf..9074ee6 100644 --- a/src/main/java/com/io/yutian/mclive/listener/LiveAdminGui.java +++ b/src/main/java/com/io/yutian/mclive/listener/LiveAdminGui.java @@ -24,130 +24,21 @@ import java.util.List; public class LiveAdminGui implements Listener { public static String invTitle = "我的世界整蛊玩法操作界面"; - @EventHandler - public void onclick(InventoryClickEvent e){ - int rawSlot = e.getRawSlot(); - Player player = (Player) e.getWhoClicked(); - String playName = player.getName(); - Inventory inv = e.getInventory(); - if(e.getView().getTitle().equalsIgnoreCase(invTitle)){ - e.setCancelled(true); - ItemStack item = e.getCurrentItem(); - if(item != null && item.getType() == Material.AIR){ - return; - } - if(rawSlot == 1){ - player.closeInventory(); - if(Main.configYml.getRoomId(playName) == null) { - player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号"); - player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1); - }else { - if (ZhuboAPI.isRoomisConnected(player)) { - return; - } - if(System.currentTimeMillis() >= 1723680000000L){ // 2024年8月15日 此插件将过期 - player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); - player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); - player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); - return; - } - String pluginName = Main.configYml.getGameMode(); - String roomId = Main.configYml.getRoomId(playName); - if (AESUtil.isVerifyCheck(player,pluginName,roomId)) { - return; - } - LiveConnectEvents event = new LiveConnectEvents(player,roomId,pluginName); - Bukkit.getPluginManager().callEvent(event); - if(ZhuboAPI.getRoomLiveName().equalsIgnoreCase("KuaiShou")) { - new BukkitRunnable() { - private int i = 0; - private int link = 1; - @Override - public void run() { - if (i >= 4) { - long cdTime = CDTimeAPI.getCD(player.getUniqueId(),"tingcloud_cd"); - if(cdTime > 0){ - player.sendMessage("§c[系统]§a听云整蛊连接成功."); - cancel(); - return; - } - PixelLiveAPI.connect(player, "127.0.0.1", 8765); - player.sendMessage("§c[系统]§a正在尝试§e[第"+link+"次]§a连接."); - link++; - i -= 4; - } - i++; - } - }.runTaskTimerAsynchronously(Main.plugin, 0L, 5L); - player.sendMessage("§c[系统]§a已在为你连接听云整蛊,请不要再次点击."); - } else { - PixelLiveAPI.connect(player,"127.0.0.1",9018); - } - if(Main.giftStatistics){ - Main.giftManage.getSqlManager().createAnchorProfile(roomId); - } - Bukkit.getConsoleSender().sendMessage("######CONNECT#####"); - player.resetTitle(); - Location loc = player.getLocation(); - loc.getWorld().playEffect(loc, Effect.MOBSPAWNER_FLAMES, 20); - player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP,1,1); - } - } - if(rawSlot == 3){ - player.closeInventory(); - player.performCommand("livegift gui"); - player.sendMessage("§c[系统]§a手持任意物品按住 §eSHIFT+Q §a打开界面"); - player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,1); - } - if(rawSlot == 5){ - player.closeInventory(); - player.performCommand("gameedit"); - player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,1); - } - if(rawSlot == 8){ - player.closeInventory(); - Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"stop"); - } - if(rawSlot == 18){ - player.closeInventory(); - Bukkit.dispatchCommand(Bukkit.getConsoleSender(),"mclive stop"); - } - if(rawSlot == 22){ - SoundsMenu.OpenGui(player,1); - } - if(rawSlot == 20){ - player.closeInventory(); - if(Main.configYml.getRoomId(playName) == null) { - player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号"); - player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1); - }else { - String roomId = Main.configYml.getRoomId(playName); - if (AESUtil.isVerifyCheck(player,Main.configYml.getGameMode(),roomId)) { - player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH,1,1); - return; - } - player.sendMessage("§c[系统]§a直播间授权通过,感谢您选择我们的产品."); - player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP,1,1); - } - } - } - } + public static HashMap offline_zhubo = new HashMap<>(); - public static HashMap offline_zhubo = new HashMap<>(); - - public static void OpenGui(Player p){ - Inventory inv = Bukkit.createInventory(null,27,invTitle); - inv.setItem(8,Stop_Server()); - inv.setItem(1,Link_Room(p)); - inv.setItem(3,Gift_BuChang()); - inv.setItem(5,Games_Edit()); - inv.setItem(18,Stop_LiveLink()); - inv.setItem(20,test_Verify(p)); - inv.setItem(22,SoundMenuButt()); + public static void OpenGui(Player p) { + Inventory inv = Bukkit.createInventory(null, 27, invTitle); + inv.setItem(8, Stop_Server()); + inv.setItem(1, Link_Room(p)); + inv.setItem(3, Gift_BuChang()); + inv.setItem(5, Games_Edit()); + inv.setItem(18, Stop_LiveLink()); + inv.setItem(20, test_Verify(p)); + inv.setItem(22, SoundMenuButt()); p.openInventory(inv); } - public static ItemStack SoundMenuButt(){ + public static ItemStack SoundMenuButt() { ItemStack item = new ItemStack(Material.DIAMOND); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e声音列表"); @@ -161,7 +52,7 @@ public class LiveAdminGui implements Listener { return item; } - public static ItemStack Stop_LiveLink(){ + public static ItemStack Stop_LiveLink() { ItemStack item = new ItemStack(Material.FLINT); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e断开主播连接"); @@ -177,7 +68,7 @@ public class LiveAdminGui implements Listener { return item; } - public static ItemStack Stop_Server(){ + public static ItemStack Stop_Server() { ItemStack item = new ItemStack(Material.PAPER); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e重启服务器"); @@ -192,8 +83,7 @@ public class LiveAdminGui implements Listener { return item; } - - public static ItemStack Gift_BuChang(){ + public static ItemStack Gift_BuChang() { ItemStack item = new ItemStack(Material.PAPER); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e礼物漏刷管理"); @@ -208,8 +98,7 @@ public class LiveAdminGui implements Listener { return item; } - - public static ItemStack Games_Edit(){ + public static ItemStack Games_Edit() { ItemStack item = new ItemStack(Material.STONECUTTER); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e整蛊游戏设置"); @@ -221,7 +110,8 @@ public class LiveAdminGui implements Listener { item.setItemMeta(meta); return item; } - public static ItemStack Link_Room(Player p){ + + public static ItemStack Link_Room(Player p) { ItemStack item = new ItemStack(Material.GOLDEN_SWORD); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §a§l连接直播间"); @@ -236,7 +126,7 @@ public class LiveAdminGui implements Listener { lore.add("§7您的直播间号: §a" + roomId); lore.add(" "); lore.add("§c★ §6鼠标点击 §7断开直播连接"); - }else { + } else { if (Main.configYml.getRoomId(p.getName()) == null) { lore.add("§c建议开播前通过下方三项"); lore.add("§c测试后再开播并连接直播间"); @@ -258,18 +148,18 @@ public class LiveAdminGui implements Listener { return item; } - public static ItemStack test_Verify(Player p){ + public static ItemStack test_Verify(Player p) { ItemStack item = new ItemStack(Material.IRON_INGOT); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e直播间授权验证"); List lore = new ArrayList<>(); - if(Main.configYml.getRoomId(p.getName()) == null) { + if (Main.configYml.getRoomId(p.getName()) == null) { lore.add("§7您的直播间号: §9尚未设置"); lore.add(" "); lore.add("§b★ §6鼠标点击 §7设置直播间"); - }else{ + } else { String roomId = Main.configYml.getRoomId(p.getName()); - lore.add("§7您的直播间号: §a"+roomId); + lore.add("§7您的直播间号: §a" + roomId); lore.add(" "); lore.add("§b★ §6鼠标点击 §7开始测试"); } @@ -278,18 +168,18 @@ public class LiveAdminGui implements Listener { return item; } - public static ItemStack getLiveRoomId(Player p){ + public static ItemStack getLiveRoomId(Player p) { ItemStack item = new ItemStack(Material.DIAMOND); ItemMeta meta = item.getItemMeta(); meta.setDisplayName("§d★ §e点击获取直播间Id"); List lore = new ArrayList<>(); - if(Main.configYml.getRoomId(p.getName()) == null) { + if (Main.configYml.getRoomId(p.getName()) == null) { lore.add("§7您的直播间号: §9尚未设置"); lore.add(" "); lore.add("§b★ §6鼠标点击 §7获取直播间Id"); - }else{ + } else { String roomId = Main.configYml.getRoomId(p.getName()); - lore.add("§7您的直播间号: §a"+roomId); + lore.add("§7您的直播间号: §a" + roomId); lore.add(" "); lore.add("§b★ §6鼠标点击 §7获取直播间Id"); } @@ -297,4 +187,114 @@ public class LiveAdminGui implements Listener { item.setItemMeta(meta); return item; } + + @EventHandler + public void onclick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Player player = (Player) e.getWhoClicked(); + String playName = player.getName(); + Inventory inv = e.getInventory(); + if (e.getView().getTitle().equalsIgnoreCase(invTitle)) { + e.setCancelled(true); + ItemStack item = e.getCurrentItem(); + if (item != null && item.getType() == Material.AIR) { + return; + } + if (rawSlot == 1) { + player.closeInventory(); + if (Main.configYml.getRoomId(playName) == null) { + player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号"); + player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 1, 1); + } else { + if (ZhuboAPI.isRoomisConnected(player)) { + return; + } + if (System.currentTimeMillis() >= 1723680000000L) { // 2024年8月15日 此插件将过期 + player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); + player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); + player.sendMessage("§c[系统]§a当前插件版本太老,请联系作者更新。"); + return; + } + String pluginName = Main.configYml.getGameMode(); + String roomId = Main.configYml.getRoomId(playName); + if (AESUtil.isVerifyCheck(player, pluginName, roomId)) { + return; + } + LiveConnectEvents event = new LiveConnectEvents(player, roomId, pluginName); + Bukkit.getPluginManager().callEvent(event); + if (ZhuboAPI.getRoomLiveName().equalsIgnoreCase("KuaiShou")) { + new BukkitRunnable() { + private int i = 0; + private int link = 1; + + @Override + public void run() { + if (i >= 4) { + long cdTime = CDTimeAPI.getCD(player.getUniqueId(), "tingcloud_cd"); + if (cdTime > 0) { + player.sendMessage("§c[系统]§a听云整蛊连接成功."); + cancel(); + return; + } + PixelLiveAPI.connect(player, "127.0.0.1", 8765); + player.sendMessage("§c[系统]§a正在尝试§e[第" + link + "次]§a连接."); + link++; + i -= 4; + } + i++; + } + }.runTaskTimerAsynchronously(Main.plugin, 0L, 5L); + player.sendMessage("§c[系统]§a已在为你连接听云整蛊,请不要再次点击."); + } else { + PixelLiveAPI.connect(player, "127.0.0.1", 9018); + } + if (Main.giftStatistics) { + Main.giftManager.getSqlManager().createAnchorProfile(roomId); + } + Bukkit.getConsoleSender().sendMessage("######CONNECT#####"); + player.resetTitle(); + Location loc = player.getLocation(); + loc.getWorld().playEffect(loc, Effect.MOBSPAWNER_FLAMES, 20); + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1, 1); + } + } + if (rawSlot == 3) { + player.closeInventory(); + player.performCommand("livegift gui"); + player.sendMessage("§c[系统]§a手持任意物品按住 §eSHIFT+Q §a打开界面"); + player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK, 1, 1); + } + if (rawSlot == 5) { + player.closeInventory(); + player.performCommand("gameedit"); + player.playSound(player.getLocation(), Sound.BLOCK_COMPARATOR_CLICK, 1, 1); + } + if (rawSlot == 8) { + player.closeInventory(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "stop"); + } + if (rawSlot == 18) { + player.closeInventory(); + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "mclive stop"); + } + if (rawSlot == 22) { + SoundsMenu.OpenGui(player, 1); + } + if (rawSlot == 20) { + player.closeInventory(); + if (Main.configYml.getRoomId(playName) == null) { + player.sendMessage("§c[系统]§a请输入命令设置直播间ID: §e/mclive 授权号"); + player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 1, 1); + } else { + String roomId = Main.configYml.getRoomId(playName); + if (AESUtil.isVerifyCheck(player, Main.configYml.getGameMode(), roomId)) { + player.playSound(player.getLocation(), Sound.ENTITY_BLAZE_DEATH, 1, 1); + return; + } + player.sendMessage("§c[系统]§a直播间授权通过,感谢您选择我们的产品."); + player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1, 1); + } + } + } + } } diff --git a/src/main/java/com/io/yutian/mclive/listener/RankListener.java b/src/main/java/com/io/yutian/mclive/listener/RankListener.java new file mode 100644 index 0000000..01b7dba --- /dev/null +++ b/src/main/java/com/io/yutian/mclive/listener/RankListener.java @@ -0,0 +1,15 @@ +package com.io.yutian.mclive.listener; + +import com.io.yutian.livemutually.liveroom.User; +import com.io.yutian.mclive.event.LiveGiftEvents; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class RankListener implements Listener { + + @EventHandler + public void onLiveGift(LiveGiftEvents event) { + User user =event.getUser(); + } + +} diff --git a/src/main/java/com/io/yutian/mclive/listener/SoundsMenu.java b/src/main/java/com/io/yutian/mclive/listener/SoundsMenu.java index c62bde9..e2648dd 100644 --- a/src/main/java/com/io/yutian/mclive/listener/SoundsMenu.java +++ b/src/main/java/com/io/yutian/mclive/listener/SoundsMenu.java @@ -20,62 +20,17 @@ import java.util.List; public class SoundsMenu implements Listener { public static String invTitle = "我的世界整蛊 - 声音列表"; - - @EventHandler - public void onClick(InventoryClickEvent e){ - int rawSlot = e.getRawSlot(); - Inventory inv = e.getInventory(); - Player p = (Player) e.getWhoClicked(); - ItemStack item = e.getCurrentItem(); - if(e.getView().getTitle().equalsIgnoreCase(invTitle)){ - e.setCancelled(true); - int Pages = e.getInventory().getItem(45).getAmount(); - if(e.getRawSlot() == 45){ - p.playSound(p.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,2); - if(Pages >= 2 && Pages <= 31){ - SoundsMenu.OpenGui(p,Pages - 1); - } else { - p.sendMessage("§c[消息]§a已经是第一页了!"); - } - } - if(e.getRawSlot() == 53){ - p.playSound(p.getLocation(), Sound.BLOCK_COMPARATOR_CLICK,1,2); - if(Pages >= 1 && Pages <= 32){ - SoundsMenu.OpenGui(p,Pages + 1); - } else { - p.sendMessage("§c[消息]§a已经是最后一页了!"); - } - } - if(rawSlot >= 0 && rawSlot < 45){ - if(item != null && item.getType() != Material.AIR) { - NBTItem nbt = new NBTItem(item); - if (nbt.hasKey("sound")) { - String sound = nbt.getString("sound"); - p.playSound(p.getLocation(), sound, 1.0F, 1.0F); - p.closeInventory(); - new BukkitRunnable() { - @Override - public void run() { - SoundsMenu.OpenGui(p, Pages); - } - }.runTaskLater(Main.plugin, 15L); - } - } - } - } - } - public static List soundList = new ArrayList<>(); - public static void OpenGui(Player p,int page){ - Inventory inv = Bukkit.createInventory(null,54,invTitle); - for (int i = 45;i < 53;i++){ - inv.setItem(i,new ItemStack(Material.WHITE_STAINED_GLASS_PANE)); + public static void OpenGui(Player p, int page) { + Inventory inv = Bukkit.createInventory(null, 54, invTitle); + for (int i = 45; i < 53; i++) { + inv.setItem(i, new ItemStack(Material.WHITE_STAINED_GLASS_PANE)); } - inv.setItem(45,Paper_Butt("§a上一页",page)); - inv.setItem(53,Paper_Butt("§a下一页",page)); + inv.setItem(45, Paper_Butt("§a上一页", page)); + inv.setItem(53, Paper_Butt("§a下一页", page)); List itemStackList = new ArrayList<>(); - for (String soundKey : soundList){ + for (String soundKey : soundList) { ItemStack item = itemShow(soundKey).clone(); itemStackList.add(item); } @@ -96,22 +51,22 @@ public class SoundsMenu implements Listener { p.openInventory(inv); } - public static ItemStack itemShow(String sound){ + public static ItemStack itemShow(String sound) { ItemStack item = new ItemStack(Material.NOTE_BLOCK); ItemMeta meta = item.getItemMeta(); - meta.setDisplayName("§6"+sound); + meta.setDisplayName("§6" + sound); List lore = new ArrayList<>(); - lore.add("§7#礼物设置声音处填入 §6"+sound+" §7即可"); + lore.add("§7#礼物设置声音处填入 §6" + sound + " §7即可"); lore.add(" "); lore.add("§a§l★ §6左键点击播放"); meta.setLore(lore); item.setItemMeta(meta); NBTItem nbtItem = new NBTItem(item); - nbtItem.setString("sound",sound); + nbtItem.setString("sound", sound); return nbtItem.getItem(); } - public static ItemStack Paper_Butt(String name,int amount){ + public static ItemStack Paper_Butt(String name, int amount) { ItemStack item = new ItemStack(Material.ARROW); item.setAmount(amount); ItemMeta meta = item.getItemMeta(); @@ -122,4 +77,48 @@ public class SoundsMenu implements Listener { item.setItemMeta(meta); return item; } + + @EventHandler + public void onClick(InventoryClickEvent e) { + int rawSlot = e.getRawSlot(); + Inventory inv = e.getInventory(); + Player p = (Player) e.getWhoClicked(); + ItemStack item = e.getCurrentItem(); + if (e.getView().getTitle().equalsIgnoreCase(invTitle)) { + e.setCancelled(true); + int Pages = e.getInventory().getItem(45).getAmount(); + if (e.getRawSlot() == 45) { + p.playSound(p.getLocation(), Sound.BLOCK_COMPARATOR_CLICK, 1, 2); + if (Pages >= 2 && Pages <= 31) { + SoundsMenu.OpenGui(p, Pages - 1); + } else { + p.sendMessage("§c[消息]§a已经是第一页了!"); + } + } + if (e.getRawSlot() == 53) { + p.playSound(p.getLocation(), Sound.BLOCK_COMPARATOR_CLICK, 1, 2); + if (Pages >= 1 && Pages <= 32) { + SoundsMenu.OpenGui(p, Pages + 1); + } else { + p.sendMessage("§c[消息]§a已经是最后一页了!"); + } + } + if (rawSlot >= 0 && rawSlot < 45) { + if (item != null && item.getType() != Material.AIR) { + NBTItem nbt = new NBTItem(item); + if (nbt.hasKey("sound")) { + String sound = nbt.getString("sound"); + p.playSound(p.getLocation(), sound, 1.0F, 1.0F); + p.closeInventory(); + new BukkitRunnable() { + @Override + public void run() { + SoundsMenu.OpenGui(p, Pages); + } + }.runTaskLater(Main.plugin, 15L); + } + } + } + } + } } diff --git a/src/main/java/com/io/yutian/mclive/live/LiveEvent.java b/src/main/java/com/io/yutian/mclive/live/LiveEvent.java index cd7b7cc..2e444d4 100644 --- a/src/main/java/com/io/yutian/mclive/live/LiveEvent.java +++ b/src/main/java/com/io/yutian/mclive/live/LiveEvent.java @@ -1,10 +1,10 @@ package com.io.yutian.mclive.live; -import com.io.yutian.mclive.util.ConfigYml; import com.io.yutian.mclive.Main; -import com.io.yutian.mclive.data.GiftManage; +import com.io.yutian.livemutually.manager.GiftManager; import com.io.yutian.mclive.event.*; import com.io.yutian.mclive.listener.LiveAdminGui; +import com.io.yutian.mclive.util.ConfigYml; import net.md_5.bungee.api.ChatMessageType; import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; @@ -16,9 +16,12 @@ public class LiveEvent implements Listener { private ConfigYml configYml; - public LiveEvent(ConfigYml configYml){this.configYml = configYml;} + public LiveEvent(ConfigYml configYml) { + this.configYml = configYml; + } + @EventHandler - public void onGift(LiveGiftEvents e){ + public void onGift(LiveGiftEvents e) { if (Main.check_plugin) { LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1); String type = "礼物"; @@ -26,13 +29,13 @@ public class LiveEvent implements Listener { String gift_name = e.getName(); long gift_amount = e.getAmount(); if (Main.giftStatistics && ConfigYml.mysqlState) { - GiftManage giftManage = Main.giftManage; + GiftManager giftManager = Main.giftManager; String name = e.getPlayer().getName(); - int trillGift = giftManage.getGiftMoney(gift_name); + int trillGift = giftManager.getGiftMoney(gift_name); if (gift_amount >= 2) { - giftManage.addTotalMoney(name,trillGift, gift_amount); + giftManager.addTotalMoney(name, trillGift, gift_amount); } else { - giftManage.addTotalMoney(name,trillGift); + giftManager.addTotalMoney(name, trillGift); } } // Bukkit.getConsoleSender().sendMessage("[直播互动 " + e.getPlayer().getName() + "] 类型: " + type + " 用户: " + audience + " 礼物: " + gift_name + "x" + gift_amount); @@ -41,24 +44,24 @@ public class LiveEvent implements Listener { @EventHandler//关注 public void onMcLive(LiveFollowEvents e) { - LiveAdminGui.offline_zhubo.put(e.getPlayer(),1); + LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1); String type = "关注"; String audience = e.getUser().nickName(); - Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience); + Bukkit.getConsoleSender().sendMessage("[直播互动 " + e.getPlayer().getName() + "] 类型: " + type + " 用户: " + audience); } @EventHandler//信息 public void onMcLive(LiveChatEvents e) { - LiveAdminGui.offline_zhubo.put(e.getPlayer(),1); + LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1); String type = "信息"; String audience = e.getUser().nickName(); String message = e.getContent(); - Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience + " 信息: " + message); + Bukkit.getConsoleSender().sendMessage("[直播互动 " + e.getPlayer().getName() + "] 类型: " + type + " 用户: " + audience + " 信息: " + message); } @EventHandler//点赞 public void onGuanzhu(LiveLikeEvents e) { - LiveAdminGui.offline_zhubo.put(e.getPlayer(),1); + LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1); String type = "点赞"; String audience = e.getUser().nickName(); long gift_amount = e.getCount(); @@ -68,10 +71,10 @@ public class LiveEvent implements Listener { @EventHandler//进入 public void onMcLive(LiveEnterEvents e) { Player player = e.getPlayer(); - LiveAdminGui.offline_zhubo.put(e.getPlayer(),1); + LiveAdminGui.offline_zhubo.put(e.getPlayer(), 1); String type = "进入"; String audience = e.getUser().nickName(); - Bukkit.getConsoleSender().sendMessage("[直播互动 "+e.getPlayer().getName()+"] 类型: " + type + " 用户: " + audience); - player.spigot().sendMessage(ChatMessageType.ACTION_BAR,new TextComponent("§6" + Main.HideName(audience) + "来了")); + Bukkit.getConsoleSender().sendMessage("[直播互动 " + e.getPlayer().getName() + "] 类型: " + type + " 用户: " + audience); + player.spigot().sendMessage(ChatMessageType.ACTION_BAR, new TextComponent("§6" + Main.HideName(audience) + "来了")); } } diff --git a/src/main/java/com/io/yutian/mclive/live/ModEvent.java b/src/main/java/com/io/yutian/mclive/live/ModEvent.java index 1407c34..e346ffd 100644 --- a/src/main/java/com/io/yutian/mclive/live/ModEvent.java +++ b/src/main/java/com/io/yutian/mclive/live/ModEvent.java @@ -10,15 +10,15 @@ import org.bukkit.event.Listener; public class ModEvent implements Listener { @EventHandler - public void onWsMessage(WSMessageEvent e){ + public void onWsMessage(WSMessageEvent e) { Player player = e.getPlayer(); String s = e.getMessage(); - MessageUtil.parse(player,s); + MessageUtil.parse(player, s); } @EventHandler - public void onKey(KeyInputEvent e){ + public void onKey(KeyInputEvent e) { Player player = e.getPlayer(); } } diff --git a/src/main/java/com/io/yutian/mclive/util/ConfigYml.java b/src/main/java/com/io/yutian/mclive/util/ConfigYml.java index 83be542..551741a 100644 --- a/src/main/java/com/io/yutian/mclive/util/ConfigYml.java +++ b/src/main/java/com/io/yutian/mclive/util/ConfigYml.java @@ -21,13 +21,13 @@ public class ConfigYml { } this.MainDebug = yml.getBoolean("MainDebug"); this.GameMode = yml.getString("GameMode"); - Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7对接游戏: §e"+ this.GameMode); + Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7对接游戏: §e" + this.GameMode); if (yml.getConfigurationSection("LiveId") != null) { HashMap hashMap = new HashMap<>(); for (String playName : yml.getConfigurationSection("LiveId").getKeys(false)) { String live_id = yml.getString("LiveId." + playName); hashMap.put(playName, live_id); - Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7主播: §e"+ playName + " - " + live_id); + Bukkit.getConsoleSender().sendMessage("§b[整蛊MC直播] §7主播: §e" + playName + " - " + live_id); } this.RoomId_Map = hashMap; } @@ -54,21 +54,28 @@ public class ConfigYml { Main.plugin.saveConfig(); } - public boolean isMainDebug() {return MainDebug;} + public boolean isMainDebug() { + return MainDebug; + } + public void setMainDebug(boolean butt) { FileConfiguration yml = Main.plugin.getConfig(); - yml.set("MainDebug",butt); + yml.set("MainDebug", butt); Main.plugin.saveConfig(); MainDebug = butt; } - public long getGifts_delay() {return gifts_delay;} + public long getGifts_delay() { + return gifts_delay; + } + public void setGifts_delay(long gifts_delay) { FileConfiguration yml = Main.plugin.getConfig(); - yml.set("GiftDelay",gifts_delay); + yml.set("GiftDelay", gifts_delay); Main.plugin.saveConfig(); this.gifts_delay = gifts_delay; } + public String getGameMode() { return this.GameMode; } @@ -83,8 +90,9 @@ public class ConfigYml { public HashMap getRoomId_Map() { return this.RoomId_Map; } + public void setRoomId(String playName, String roomId) { - this.RoomId_Map.put(playName,roomId); + this.RoomId_Map.put(playName, roomId); } } diff --git a/src/main/java/com/io/yutian/mclive/util/FileUtil.java b/src/main/java/com/io/yutian/mclive/util/FileUtil.java new file mode 100644 index 0000000..51b6041 --- /dev/null +++ b/src/main/java/com/io/yutian/mclive/util/FileUtil.java @@ -0,0 +1,34 @@ +package com.io.yutian.mclive.util; + +import java.io.*; +import java.net.URL; +import java.util.Base64; + +public class FileUtil { + + public static void downloadImage(String imageUrl, File destinationFile, Runnable callback) { + try (InputStream inputStream = new URL(imageUrl).openStream(); + OutputStream outputStream = new FileOutputStream(destinationFile)) { + byte[] buffer = new byte[2048]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, length); + } + callback.run(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static String convertImageToBase64(File imageFile) { + try (FileInputStream fileInputStream = new FileInputStream(imageFile)) { + byte[] imageBytes = new byte[(int) imageFile.length()]; + fileInputStream.read(imageBytes); + return Base64.getEncoder().encodeToString(imageBytes); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/main/java/com/io/yutian/mclive/util/MessageUtil.java b/src/main/java/com/io/yutian/mclive/util/MessageUtil.java index c3fd0a2..6b3e9ea 100644 --- a/src/main/java/com/io/yutian/mclive/util/MessageUtil.java +++ b/src/main/java/com/io/yutian/mclive/util/MessageUtil.java @@ -1,13 +1,21 @@ package com.io.yutian.mclive.util; import cn.hamster3.cdapi.CDTimeAPI; -import com.io.yutian.livemutually.manager.GiftData; -import com.io.yutian.livemutually.manager.UserData; -import com.io.yutian.livemutually.wss.*; +import com.io.yutian.livemutually.manager.GiftManager; +import com.io.yutian.livemutually.manager.RankManager; +import com.io.yutian.livemutually.manager.UserManager; +import com.io.yutian.mclive.data.GiftData; +import com.io.yutian.mclive.data.UserData; +import com.io.yutian.livemutually.wss.KSAPILiveRoomWatcher; +import com.io.yutian.livemutually.wss.KuaiShouChat; +import com.io.yutian.livemutually.wss.KuaiShouLike; +import com.io.yutian.livemutually.wss.KuaiShouUser; import com.io.yutian.mclive.Main; +import com.io.yutian.mclive.data.UserResourceData; import com.io.yutian.mclive.event.LiveGiftEvents; import com.io.yutian.mclive.event.ZhuboAPI; import com.io.yutian.verify.AESUtil; +import json.JSONArray; import json.JSONObject; import org.bukkit.Bukkit; import org.bukkit.entity.Player; @@ -19,18 +27,24 @@ import java.util.HashMap; public class MessageUtil { - public static void parse(Player player,String py_data){ - if(py_data.isEmpty()){return;} - if(ZhuboAPI.getRoomLiveName().equalsIgnoreCase("KuaiShou")){ + // H8dyaR95ZJwZ6PikZ1qmoIY1 密匙 + private static final String AES_KEY = "01001000001110000110010001111001011000010101001000111001001101010101101001001010011101110101101000110110010100000110100101101"; // 与Python中相同的密钥 + public static HashMap userDataMap = new HashMap<>(); + + public static void parse(Player player, String py_data) { + if (py_data.isEmpty()) { + return; + } + if (ZhuboAPI.getRoomLiveName().equalsIgnoreCase("KuaiShou")) { JSONObject jsonObject = new JSONObject(py_data); if (py_data.contains("type")) { - long cdTime = CDTimeAPI.getCD(player.getUniqueId(),"tingcloud_cd"); - if(cdTime < 0) { - CDTimeAPI.setPlayerCD(player.getUniqueId(),"tingcloud_cd",1000 * 10); + long cdTime = CDTimeAPI.getCD(player.getUniqueId(), "tingcloud_cd"); + if (cdTime < 0) { + CDTimeAPI.setPlayerCD(player.getUniqueId(), "tingcloud_cd", 1000 * 10); Bukkit.getConsoleSender().sendMessage("§a[直播连接助手] 听云整蛊弹幕正常抓取中..."); } String type = jsonObject.getString("type"); - if(type.equalsIgnoreCase("礼物")){ + if (type.equalsIgnoreCase("礼物")) { String nickName = jsonObject.getString("昵称"); String giftName = jsonObject.getString("礼物名称"); int newCount = jsonObject.getInt("礼物数量"); @@ -48,26 +62,27 @@ public class MessageUtil { String message = "§6[日志 - 礼物触发] §f" + player.getName() + " >>> " + nickName + " = §a" + giftName + " 数量: " + count_color + newCount; Bukkit.getConsoleSender().sendMessage(message); LiveGiftEvents event = new LiveGiftEvents(player, new KuaiShouUser(nickName), giftName, newCount); + Bukkit.getPluginManager().callEvent(event); } - }else if(type.equalsIgnoreCase("发言")){ + } else if (type.equalsIgnoreCase("发言")) { String userName = jsonObject.getString("昵称"); String message = jsonObject.getString("弹幕"); KSAPILiveRoomWatcher ksapiLiveRoomWatcher = new KSAPILiveRoomWatcher(player); ksapiLiveRoomWatcher.callChat(new KuaiShouChat(userName, message)); - }else if(type.equalsIgnoreCase("点赞")){ + } else if (type.equalsIgnoreCase("点赞")) { String userName = jsonObject.getString("昵称"); KSAPILiveRoomWatcher ksapiLiveRoomWatcher = new KSAPILiveRoomWatcher(player); ksapiLiveRoomWatcher.callLike(new KuaiShouLike(userName, 1)); } } else { - long cdTime = CDTimeAPI.getCD(player.getUniqueId(),"tingcloud_cd"); - if(cdTime < 0) { - CDTimeAPI.setPlayerCD(player.getUniqueId(),"tingcloud_cd",1000 * 10); + long cdTime = CDTimeAPI.getCD(player.getUniqueId(), "tingcloud_cd"); + if (cdTime < 0) { + CDTimeAPI.setPlayerCD(player.getUniqueId(), "tingcloud_cd", 1000 * 10); Bukkit.getConsoleSender().sendMessage("§a[直播连接助手] 听云整蛊弹幕正常抓取中..."); } } - }else { + } else { String s; try { s = decrypt_ecb(py_data); @@ -82,7 +97,10 @@ public class MessageUtil { return; } JSONObject dataObject = new JSONObject(jsonObject.getString("data")); - String nickName = dataObject.getJSONObject("user").getString("nickName"); + JSONObject userJSONObject = dataObject.getJSONObject("user"); + String nickName = userJSONObject.getString("nickName"); + UserResourceData userResourceData = parseUserResourceData(userJSONObject); + UserManager.setUserResourceData(nickName, userResourceData); // 礼物名 String giftName = dataObject.getJSONObject("gift").getString("name"); // 礼物数量 @@ -138,6 +156,7 @@ public class MessageUtil { Bukkit.getConsoleSender().sendMessage(message); LiveGiftEvents event = new LiveGiftEvents(player, new KuaiShouUser(nickName), giftName, newCount); Bukkit.getPluginManager().callEvent(event); + RankManager.addUserAmount(nickName, Main.giftManager.getGiftMoney(giftName) * newCount); } } else if (type.equalsIgnoreCase("chat_message")) { if (!ZhuboAPI.isWsRoomIdSame(player)) { @@ -171,7 +190,7 @@ public class MessageUtil { ksapiLiveRoomWatcher.callUser(new KuaiShouUser(nickName)); } else if (type.equalsIgnoreCase("live_id")) { JSONObject dataObject = jsonObject.getJSONObject("data"); - String wsRoomId = dataObject.getString("id").replace(" ","").replace("\n",""); + String wsRoomId = dataObject.getString("id").replace(" ", "").replace("\n", ""); if (ZhuboAPI.wsRoomIdSame.get(player) == null) { String roomId = Main.configYml.getRoomId(player.getName()); if (!wsRoomId.equalsIgnoreCase(roomId)) { @@ -192,21 +211,35 @@ public class MessageUtil { public static void sendConsoleMessage(String message) { // Bukkit.getConsoleSender().sendMessage("§6[弹幕监听] " + message); } - public static HashMap userDataMap = new HashMap<>(); - // H8dyaR95ZJwZ6PikZ1qmoIY1 密匙 - private static final String AES_KEY = "01001000001110000110010001111001011000010101001000111001001101010101101001001010011101110101101000110110010100000110100101101"; // 与Python中相同的密钥 public static String decrypt_ecb(String encryptedMessage) throws Exception { // Base64解码 byte[] decodedMessage = Base64.getDecoder().decode(encryptedMessage); // AES解密 - Cipher cipher = Cipher.getInstance(AESUtil.convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100"+AESUtil.DEFAULT_CIPHER_ALGORITHM)); - SecretKeySpec secretKeySpec = new SecretKeySpec(AESUtil.convertString(AES_KEY+"0110101101000110001011100010110110101101111010010010101100100110001").getBytes(), "AES"); + Cipher cipher = Cipher.getInstance(AESUtil.convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100" + AESUtil.DEFAULT_CIPHER_ALGORITHM)); + SecretKeySpec secretKeySpec = new SecretKeySpec(AESUtil.convertString(AES_KEY + "0110101101000110001011100010110110101101111010010010101100100110001").getBytes(), "AES"); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec); byte[] decryptedBytes = cipher.doFinal(decodedMessage); // 转换为字符串 return new String(decryptedBytes, "UTF-8"); } + + private static UserResourceData parseUserResourceData(JSONObject jsonObject) { + String nickname = jsonObject.getString("nickName"); + String avatarThumb = jsonObject.getJSONObject("AvatarThumb").getJSONArray("urlListList").getString(0); + int level = 1; + if (jsonObject.has("BadgeImageList")) { + JSONArray jsonArray = jsonObject.getJSONArray("BadgeImageList"); + for (int i = 0; i < jsonArray.length(); i++) { + JSONObject jsonObject1 = jsonArray.getJSONObject(i); + String url = jsonObject1.getString("uri"); + if (url.startsWith("webcast/new_user_grade_level")) { + level = jsonObject1.getJSONObject("content").getInt("level"); + } + } + } + return new UserResourceData(nickname, avatarThumb, level); + } } diff --git a/src/main/java/com/io/yutian/mclive/util/SqlUtil.java b/src/main/java/com/io/yutian/mclive/util/SqlUtil.java index 210c2d2..6e7c23a 100644 --- a/src/main/java/com/io/yutian/mclive/util/SqlUtil.java +++ b/src/main/java/com/io/yutian/mclive/util/SqlUtil.java @@ -26,9 +26,9 @@ public class SqlUtil extends MegumiSQL { public void openConnection() { try { String mcVersion = Main.getMinecraftVersion(); - if(mcVersion.contains("1_20")){ + if (mcVersion.contains("1_20")) { Class.forName("com.mysql.cj.jdbc.Driver"); // 1.20.4的mysql路径 - }else{ + } else { Class.forName("com.mysql.jdbc.Driver"); // 1.18.2的mysql路径 } this.connection = DriverManager.getConnection("jdbc:mysql://" + this.hostname + ":" + this.port + "/" + this.database + "?useSSL=false", this.username, this.password); diff --git a/src/main/java/com/io/yutian/verify/AESUtil.java b/src/main/java/com/io/yutian/verify/AESUtil.java index 8771f6f..f607392 100644 --- a/src/main/java/com/io/yutian/verify/AESUtil.java +++ b/src/main/java/com/io/yutian/verify/AESUtil.java @@ -14,9 +14,9 @@ import java.util.Base64; public class AESUtil { - private static final String KEY_ALGORITHM = "AES"; // AES/ECB/PKCS5Padding public static final String DEFAULT_CIPHER_ALGORITHM = "101101000011010100110011010101010000011000010110010001100100011010010110111001100111"; + private static final String KEY_ALGORITHM = "AES"; // 将二进制字符串转换为普通字符串 public static String convertString(String binaryString) { @@ -34,25 +34,23 @@ public class AESUtil { return result.toString(); } - public static boolean isVerifyCheck(Player p, String pluginName, String roomId){ - if(!Main.check_plugin){ + public static boolean isVerifyCheck(Player p, String pluginName, String roomId) { + if (!Main.check_plugin) { return false; } - PluginVerifyResult verifyResult = VerifyHandler.verify("127.0.0.1",pluginName,roomId); + PluginVerifyResult verifyResult = VerifyHandler.verify("127.0.0.1", pluginName, roomId); if (!verifyResult.equals(PluginVerifyResult.VERIFY_SUCCESS)) { - if(verifyResult == PluginVerifyResult.USER_STATE_DISABLE){ - Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的直播间授权已到期."); + if (verifyResult == PluginVerifyResult.USER_STATE_DISABLE) { + Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] " + p.getName() + " 的直播间授权已到期."); p.sendMessage("§c[系统]§a验证尚未通过,您的直播间授权已到期."); - } else - if(verifyResult == PluginVerifyResult.FAIL_CODE){ - Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的直播间尚未进行授权. "+roomId); - p.sendMessage("§c[系统]§a验证尚未通过,直播间§e<"+roomId+">§a尚未进行授权."); - } else - if(verifyResult == PluginVerifyResult.FAIL_TIMEOUT){ - Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的本地网络有问题,无法连接验证服务器."); + } else if (verifyResult == PluginVerifyResult.FAIL_CODE) { + Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] " + p.getName() + " 的直播间尚未进行授权. " + roomId); + p.sendMessage("§c[系统]§a验证尚未通过,直播间§e<" + roomId + ">§a尚未进行授权."); + } else if (verifyResult == PluginVerifyResult.FAIL_TIMEOUT) { + Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] " + p.getName() + " 的本地网络有问题,无法连接验证服务器."); p.sendMessage("§c[系统]§a验证尚未通过,您当前的网络环境有问题."); } else { - Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] "+p.getName()+" 的无法通过验证. "+verifyResult); + Bukkit.getConsoleSender().sendMessage("[验证日志 - 拦截] " + p.getName() + " 的无法通过验证. " + verifyResult); p.sendMessage("§c[系统]§a验证尚未通过,无法连接直播间,请联系管理员。§c§l#" + verifyResult); } return true; @@ -68,7 +66,7 @@ public class AESUtil { public static String encrypt(String content, String key) { try { - Cipher cipher = Cipher.getInstance(convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100"+DEFAULT_CIPHER_ALGORITHM)); + Cipher cipher = Cipher.getInstance(convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100" + DEFAULT_CIPHER_ALGORITHM)); byte[] byteContent = content.getBytes("utf-8"); cipher.init(Cipher.ENCRYPT_MODE, getSecretKey(key)); byte[] result = cipher.doFinal(byteContent); @@ -80,7 +78,7 @@ public class AESUtil { public static String decrypt(String content, String key) { try { - Cipher cipher = Cipher.getInstance(convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100"+DEFAULT_CIPHER_ALGORITHM)); + Cipher cipher = Cipher.getInstance(convertString("0100000101000101010100110010111101000101010000110100001000101111010100000100" + DEFAULT_CIPHER_ALGORITHM)); cipher.init(Cipher.DECRYPT_MODE, getSecretKey(key)); byte[] result = cipher.doFinal(base642Byte(content)); return new String(result, "utf-8"); diff --git a/src/main/java/com/io/yutian/verify/VerifyHandler.java b/src/main/java/com/io/yutian/verify/VerifyHandler.java index a991bfb..06f2503 100644 --- a/src/main/java/com/io/yutian/verify/VerifyHandler.java +++ b/src/main/java/com/io/yutian/verify/VerifyHandler.java @@ -4,7 +4,6 @@ import json.JSONObject; import java.io.BufferedReader; import java.io.InputStreamReader; -import java.net.ConnectException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLConnection; @@ -41,24 +40,26 @@ public class VerifyHandler { return PluginVerifyResult.UNKNOWN; } } - public static String getServerIp(){ + + public static String getServerIp() { String a = "47."; String b = "111."; String c = "169."; String d = "142"; String e = "82"; String f = "81"; - return a+b+c+d+":"+e+f; + return a + b + c + d + ":" + e + f; } + private static String get(String plugin, String code) throws Exception { - String url = "/verify?plugin="+plugin+"&code="+code; - URL realUrl = new URL("http://"+getServerIp()+url); + String url = "/verify?plugin=" + plugin + "&code=" + code; + URL realUrl = new URL("http://" + getServerIp() + url); URLConnection connection = realUrl.openConnection(); connection.setConnectTimeout(5000); connection.setReadTimeout(15000); connection.setRequestProperty("accept", "*/*"); connection.setRequestProperty("connection", "Keep-Alive"); - connection.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); + connection.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); connection.connect(); BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream(), "utf-8")); StringBuilder stringBuilder = new StringBuilder(); diff --git a/src/main/java/json/JSONArray.java b/src/main/java/json/JSONArray.java index 8eb9129..b5f594f 100644 --- a/src/main/java/json/JSONArray.java +++ b/src/main/java/json/JSONArray.java @@ -94,17 +94,15 @@ public class JSONArray implements Iterable { /** * Construct a JSONArray from a JSONTokener. * - * @param x - * A JSONTokener - * @throws JSONException - * If there is a syntax error. + * @param x A JSONTokener + * @throws JSONException If there is a syntax error. */ public JSONArray(JSONTokener x) throws JSONException { this(); if (x.nextClean() != '[') { throw x.syntaxError("A JSONArray text must start with '['"); } - + char nextChar = x.nextClean(); if (nextChar == 0) { // array is unclosed. No ']' found, instead EOF @@ -112,7 +110,7 @@ public class JSONArray implements Iterable { } if (nextChar != ']') { x.back(); - for (;;) { + for (; ; ) { if (x.nextClean() == ',') { x.back(); this.myArrayList.add(JSONObject.NULL); @@ -121,24 +119,24 @@ public class JSONArray implements Iterable { this.myArrayList.add(x.nextValue()); } switch (x.nextClean()) { - case 0: - // array is unclosed. No ']' found, instead EOF - throw x.syntaxError("Expected a ',' or ']'"); - case ',': - nextChar = x.nextClean(); - if (nextChar == 0) { + case 0: // array is unclosed. No ']' found, instead EOF throw x.syntaxError("Expected a ',' or ']'"); - } - if (nextChar == ']') { + case ',': + nextChar = x.nextClean(); + if (nextChar == 0) { + // array is unclosed. No ']' found, instead EOF + throw x.syntaxError("Expected a ',' or ']'"); + } + if (nextChar == ']') { + return; + } + x.back(); + break; + case ']': return; - } - x.back(); - break; - case ']': - return; - default: - throw x.syntaxError("Expected a ',' or ']'"); + default: + throw x.syntaxError("Expected a ',' or ']'"); } } } @@ -147,12 +145,10 @@ public class JSONArray implements Iterable { /** * Construct a JSONArray from a source JSON text. * - * @param source - * A string that begins with [ (left - * bracket) and ends with ] - *  (right bracket). - * @throws JSONException - * If there is a syntax error. + * @param source A string that begins with [ (left + * bracket) and ends with ] + *  (right bracket). + * @throws JSONException If there is a syntax error. */ public JSONArray(String source) throws JSONException { this(new JSONTokener(source)); @@ -161,31 +157,26 @@ public class JSONArray implements Iterable { /** * Construct a JSONArray from a Collection. * - * @param collection - * A Collection. + * @param collection A Collection. */ public JSONArray(Collection collection) { if (collection == null) { this.myArrayList = new ArrayList(); } else { this.myArrayList = new ArrayList(collection.size()); - for (Object o: collection){ - this.myArrayList.add(JSONObject.wrap(o)); - } + for (Object o : collection) { + this.myArrayList.add(JSONObject.wrap(o)); + } } } /** * Construct a JSONArray from an array. * - * @param array - * Array. If the parameter passed is null, or not an array, an - * exception will be thrown. - * - * @throws JSONException - * If not an array or if an array value is non-finite number. - * @throws NullPointerException - * Thrown if the array parameter is null. + * @param array Array. If the parameter passed is null, or not an array, an + * exception will be thrown. + * @throws JSONException If not an array or if an array value is non-finite number. + * @throws NullPointerException Thrown if the array parameter is null. */ public JSONArray(Object array) throws JSONException { this(); @@ -201,6 +192,41 @@ public class JSONArray implements Iterable { } } + /** + * Create a new JSONException in a common format for incorrect conversions. + * + * @param idx index of the item + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + int idx, + String valueType, + Throwable cause) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + "." + , cause); + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * + * @param idx index of the item + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + int idx, + String valueType, + Object value, + Throwable cause) { + return new JSONException( + "JSONArray[" + idx + "] is not a " + valueType + " (" + value + ")." + , cause); + } + @Override public Iterator iterator() { return this.myArrayList.iterator(); @@ -209,11 +235,9 @@ public class JSONArray implements Iterable { /** * Get the object value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return An object value. - * @throws JSONException - * If there is no value for the index. + * @throws JSONException If there is no value for the index. */ public Object get(int index) throws JSONException { Object object = this.opt(index); @@ -227,22 +251,20 @@ public class JSONArray implements Iterable { * Get the boolean value associated with an index. The string values "true" * and "false" are converted to boolean. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The truth. - * @throws JSONException - * If there is no value for the index or if the value is not - * convertible to boolean. + * @throws JSONException If there is no value for the index or if the value is not + * convertible to boolean. */ public boolean getBoolean(int index) throws JSONException { Object object = this.get(index); if (object.equals(Boolean.FALSE) || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { + .equalsIgnoreCase("false"))) { return false; } else if (object.equals(Boolean.TRUE) || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { + .equalsIgnoreCase("true"))) { return true; } throw wrongValueFormatException(index, "boolean", null); @@ -251,17 +273,15 @@ public class JSONArray implements Iterable { /** * Get the double value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a number. */ public double getDouble(int index) throws JSONException { final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).doubleValue(); + if (object instanceof Number) { + return ((Number) object).doubleValue(); } try { return Double.parseDouble(object.toString()); @@ -273,17 +293,15 @@ public class JSONArray implements Iterable { /** * Get the float value associated with a key. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. */ public float getFloat(int index) throws JSONException { final Object object = this.get(index); - if(object instanceof Number) { - return ((Float)object).floatValue(); + if (object instanceof Number) { + return ((Float) object).floatValue(); } try { return Float.parseFloat(object.toString()); @@ -295,18 +313,16 @@ public class JSONArray implements Iterable { /** * Get the Number value associated with a key. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. */ public Number getNumber(int index) throws JSONException { Object object = this.get(index); try { if (object instanceof Number) { - return (Number)object; + return (Number) object; } return JSONObject.stringToNumber(object.toString()); } catch (Exception e) { @@ -316,21 +332,17 @@ public class JSONArray implements Iterable { /** * Get the enum value associated with an index. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param index The index must be between 0 and length() - 1. * @return The enum value at the index location - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an enum. + * @throws JSONException if the key is not found or if the value cannot be converted + * to an enum. */ public > E getEnum(Class clazz, int index) throws JSONException { E val = optEnum(clazz, index); - if(val==null) { + if (val == null) { // JSONException should really take a throwable argument. // If it did, I would re-implement this with the Enum.valueOf // method and place any thrown exception in the JSONException @@ -346,17 +358,15 @@ public class JSONArray implements Iterable { * will be used. See notes on the constructor for conversion issues that * may arise. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a BigDecimal. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a BigDecimal. */ - public BigDecimal getBigDecimal (int index) throws JSONException { + public BigDecimal getBigDecimal(int index) throws JSONException { Object object = this.get(index); BigDecimal val = JSONObject.objectToBigDecimal(object, null); - if(val == null) { + if (val == null) { throw wrongValueFormatException(index, "BigDecimal", object, null); } return val; @@ -365,17 +375,15 @@ public class JSONArray implements Iterable { /** * Get the BigInteger value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a BigInteger. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a BigInteger. */ - public BigInteger getBigInteger (int index) throws JSONException { + public BigInteger getBigInteger(int index) throws JSONException { Object object = this.get(index); BigInteger val = JSONObject.objectToBigInteger(object, null); - if(val == null) { + if (val == null) { throw wrongValueFormatException(index, "BigInteger", object, null); } return val; @@ -384,16 +392,14 @@ public class JSONArray implements Iterable { /** * Get the int value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. - * @throws JSONException - * If the key is not found or if the value is not a number. + * @throws JSONException If the key is not found or if the value is not a number. */ public int getInt(int index) throws JSONException { final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).intValue(); + if (object instanceof Number) { + return ((Number) object).intValue(); } try { return Integer.parseInt(object.toString()); @@ -405,12 +411,10 @@ public class JSONArray implements Iterable { /** * Get the JSONArray associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return A JSONArray value. - * @throws JSONException - * If there is no value for the index. or if the value is not a - * JSONArray + * @throws JSONException If there is no value for the index. or if the value is not a + * JSONArray */ public JSONArray getJSONArray(int index) throws JSONException { Object object = this.get(index); @@ -423,12 +427,10 @@ public class JSONArray implements Iterable { /** * Get the JSONObject associated with an index. * - * @param index - * subscript + * @param index subscript * @return A JSONObject value. - * @throws JSONException - * If there is no value for the index or if the value is not a - * JSONObject + * @throws JSONException If there is no value for the index or if the value is not a + * JSONObject */ public JSONObject getJSONObject(int index) throws JSONException { Object object = this.get(index); @@ -441,17 +443,15 @@ public class JSONArray implements Iterable { /** * Get the long value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. - * @throws JSONException - * If the key is not found or if the value cannot be converted - * to a number. + * @throws JSONException If the key is not found or if the value cannot be converted + * to a number. */ public long getLong(int index) throws JSONException { final Object object = this.get(index); - if(object instanceof Number) { - return ((Number)object).longValue(); + if (object instanceof Number) { + return ((Number) object).longValue(); } try { return Long.parseLong(object.toString()); @@ -463,11 +463,9 @@ public class JSONArray implements Iterable { /** * Get the string associated with an index. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return A string value. - * @throws JSONException - * If there is no string value for the index. + * @throws JSONException If there is no string value for the index. */ public String getString(int index) throws JSONException { Object object = this.get(index); @@ -480,8 +478,7 @@ public class JSONArray implements Iterable { /** * Determine if the value is null. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return true if the value at the index is null, or if there is no value. */ public boolean isNull(int index) { @@ -493,24 +490,22 @@ public class JSONArray implements Iterable { * separator string is inserted between each element. Warning: * This method assumes that the data structure is acyclical. * - * @param separator - * A string that will be inserted between the elements. + * @param separator A string that will be inserted between the elements. * @return a string. - * @throws JSONException - * If the array contains an invalid number. + * @throws JSONException If the array contains an invalid number. */ public String join(String separator) throws JSONException { int len = this.length(); if (len == 0) { return ""; } - + StringBuilder sb = new StringBuilder( - JSONObject.valueToString(this.myArrayList.get(0))); + JSONObject.valueToString(this.myArrayList.get(0))); for (int i = 1; i < len; i++) { sb.append(separator) - .append(JSONObject.valueToString(this.myArrayList.get(i))); + .append(JSONObject.valueToString(this.myArrayList.get(i))); } return sb.toString(); } @@ -527,8 +522,7 @@ public class JSONArray implements Iterable { /** * Get the optional object value associated with an index. * - * @param index - * The index must be between 0 and length() - 1. If not, null is returned. + * @param index The index must be between 0 and length() - 1. If not, null is returned. * @return An object value, or null if there is no object at that index. */ public Object opt(int index) { @@ -541,8 +535,7 @@ public class JSONArray implements Iterable { * if there is no value at that index, or if the value is not Boolean.TRUE * or the String "true". * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The truth. */ public boolean optBoolean(int index) { @@ -554,10 +547,8 @@ public class JSONArray implements Iterable { * defaultValue if there is no value at that index or if it is not a Boolean * or the String "true" or "false" (case insensitive). * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * A boolean default. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue A boolean default. * @return The truth. */ public boolean optBoolean(int index, boolean defaultValue) { @@ -573,8 +564,7 @@ public class JSONArray implements Iterable { * if there is no value for the index, or if the value is not a number and * cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. */ public double optDouble(int index) { @@ -586,10 +576,8 @@ public class JSONArray implements Iterable { * is returned if there is no value for the index, or if the value is not a * number and cannot be converted to a number. * - * @param index - * subscript - * @param defaultValue - * The default value. + * @param index subscript + * @param defaultValue The default value. * @return The value. */ public double optDouble(int index, double defaultValue) { @@ -609,8 +597,7 @@ public class JSONArray implements Iterable { * if there is no value for the index, or if the value is not a number and * cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. */ public float optFloat(int index) { @@ -622,10 +609,8 @@ public class JSONArray implements Iterable { * is returned if there is no value for the index, or if the value is not a * number and cannot be converted to a number. * - * @param index - * subscript - * @param defaultValue - * The default value. + * @param index subscript + * @param defaultValue The default value. * @return The value. */ public float optFloat(int index, float defaultValue) { @@ -645,8 +630,7 @@ public class JSONArray implements Iterable { * there is no value for the index, or if the value is not a number and * cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. */ public int optInt(int index) { @@ -658,10 +642,8 @@ public class JSONArray implements Iterable { * returned if there is no value for the index, or if the value is not a * number and cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. * @return The value. */ public int optInt(int index, int defaultValue) { @@ -674,13 +656,10 @@ public class JSONArray implements Iterable { /** * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param index The index must be between 0 and length() - 1. * @return The enum value at the index location or null if not found */ public > E optEnum(Class clazz, int index) { @@ -689,17 +668,13 @@ public class JSONArray implements Iterable { /** * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default in case the value is not found + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default in case the value is not found * @return The enum value at the index location or defaultValue if - * the value is not found or cannot be assigned to clazz + * the value is not found or cannot be assigned to clazz */ public > E optEnum(Class clazz, int index, E defaultValue) { try { @@ -722,14 +697,12 @@ public class JSONArray implements Iterable { } /** - * Get the optional BigInteger value associated with an index. The - * defaultValue is returned if there is no value for the index, or if the + * Get the optional BigInteger value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the * value is not a number and cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. * @return The value. */ public BigInteger optBigInteger(int index, BigInteger defaultValue) { @@ -738,17 +711,15 @@ public class JSONArray implements Iterable { } /** - * Get the optional BigDecimal value associated with an index. The - * defaultValue is returned if there is no value for the index, or if the + * Get the optional BigDecimal value associated with an index. The + * defaultValue is returned if there is no value for the index, or if the * value is not a number and cannot be converted to a number. If the value * is float or double, the the {@link BigDecimal#BigDecimal(double)} * constructor will be used. See notes on the constructor for conversion * issues that may arise. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. * @return The value. */ public BigDecimal optBigDecimal(int index, BigDecimal defaultValue) { @@ -759,10 +730,9 @@ public class JSONArray implements Iterable { /** * Get the optional JSONArray associated with an index. * - * @param index - * subscript + * @param index subscript * @return A JSONArray value, or null if the index has no value, or if the - * value is not a JSONArray. + * value is not a JSONArray. */ public JSONArray optJSONArray(int index) { Object o = this.opt(index); @@ -774,8 +744,7 @@ public class JSONArray implements Iterable { * the key is not found, or null if the index has no value, or if the value * is not a JSONObject. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return A JSONObject value. */ public JSONObject optJSONObject(int index) { @@ -788,8 +757,7 @@ public class JSONArray implements Iterable { * there is no value for the index, or if the value is not a number and * cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return The value. */ public long optLong(int index) { @@ -801,10 +769,8 @@ public class JSONArray implements Iterable { * returned if there is no value for the index, or if the value is not a * number and cannot be converted to a number. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. * @return The value. */ public long optLong(int index, long defaultValue) { @@ -821,8 +787,7 @@ public class JSONArray implements Iterable { * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method * would be used in cases where type coercion of the number value is unwanted. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return An object which is the value. */ public Number optNumber(int index) { @@ -835,10 +800,8 @@ public class JSONArray implements Iterable { * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method * would be used in cases where type coercion of the number value is unwanted. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default. * @return An object which is the value. */ public Number optNumber(int index, Number defaultValue) { @@ -846,10 +809,10 @@ public class JSONArray implements Iterable { if (JSONObject.NULL.equals(val)) { return defaultValue; } - if (val instanceof Number){ + if (val instanceof Number) { return (Number) val; } - + if (val instanceof String) { try { return JSONObject.stringToNumber((String) val); @@ -865,8 +828,7 @@ public class JSONArray implements Iterable { * empty string if there is no value at that index. If the value is not a * string and is not null, then it is converted to a string. * - * @param index - * The index must be between 0 and length() - 1. + * @param index The index must be between 0 and length() - 1. * @return A String value. */ public String optString(int index) { @@ -877,10 +839,8 @@ public class JSONArray implements Iterable { * Get the optional string associated with an index. The defaultValue is * returned if the key is not found. * - * @param index - * The index must be between 0 and length() - 1. - * @param defaultValue - * The default value. + * @param index The index must be between 0 and length() - 1. + * @param defaultValue The default value. * @return A String value. */ public String optString(int index, String defaultValue) { @@ -892,8 +852,7 @@ public class JSONArray implements Iterable { /** * Append a boolean value. This increases the array's length by one. * - * @param value - * A boolean value. + * @param value A boolean value. * @return this. */ public JSONArray put(boolean value) { @@ -904,11 +863,9 @@ public class JSONArray implements Iterable { * Put a value in the JSONArray, where the value will be a JSONArray which * is produced from a Collection. * - * @param value - * A Collection value. + * @param value A Collection value. * @return this. - * @throws JSONException - * If the value is non-finite number. + * @throws JSONException If the value is non-finite number. */ public JSONArray put(Collection value) { return this.put(new JSONArray(value)); @@ -917,24 +874,20 @@ public class JSONArray implements Iterable { /** * Append a double value. This increases the array's length by one. * - * @param value - * A double value. + * @param value A double value. * @return this. - * @throws JSONException - * if the value is not finite. + * @throws JSONException if the value is not finite. */ public JSONArray put(double value) throws JSONException { return this.put(Double.valueOf(value)); } - + /** * Append a float value. This increases the array's length by one. * - * @param value - * A float value. + * @param value A float value. * @return this. - * @throws JSONException - * if the value is not finite. + * @throws JSONException if the value is not finite. */ public JSONArray put(float value) throws JSONException { return this.put(Float.valueOf(value)); @@ -943,8 +896,7 @@ public class JSONArray implements Iterable { /** * Append an int value. This increases the array's length by one. * - * @param value - * An int value. + * @param value An int value. * @return this. */ public JSONArray put(int value) { @@ -954,8 +906,7 @@ public class JSONArray implements Iterable { /** * Append an long value. This increases the array's length by one. * - * @param value - * A long value. + * @param value A long value. * @return this. */ public JSONArray put(long value) { @@ -966,13 +917,10 @@ public class JSONArray implements Iterable { * Put a value in the JSONArray, where the value will be a JSONObject which * is produced from a Map. * - * @param value - * A Map value. + * @param value A Map value. * @return this. - * @throws JSONException - * If a value in the map is non-finite number. - * @throws NullPointerException - * If a key in the map is null + * @throws JSONException If a value in the map is non-finite number. + * @throws NullPointerException If a key in the map is null */ public JSONArray put(Map value) { return this.put(new JSONObject(value)); @@ -981,13 +929,11 @@ public class JSONArray implements Iterable { /** * Append an object value. This increases the array's length by one. * - * @param value - * An object value. The value should be a Boolean, Double, - * Integer, JSONArray, JSONObject, Long, or String, or the - * JSONObject.NULL object. + * @param value An object value. The value should be a Boolean, Double, + * Integer, JSONArray, JSONObject, Long, or String, or the + * JSONObject.NULL object. * @return this. - * @throws JSONException - * If the value is non-finite number. + * @throws JSONException If the value is non-finite number. */ public JSONArray put(Object value) { JSONObject.testValidity(value); @@ -1000,13 +946,10 @@ public class JSONArray implements Iterable { * than the length of the JSONArray, then null elements will be added as * necessary to pad it out. * - * @param index - * The subscript. - * @param value - * A boolean value. + * @param index The subscript. + * @param value A boolean value. * @return this. - * @throws JSONException - * If the index is negative. + * @throws JSONException If the index is negative. */ public JSONArray put(int index, boolean value) throws JSONException { return this.put(index, value ? Boolean.TRUE : Boolean.FALSE); @@ -1016,13 +959,10 @@ public class JSONArray implements Iterable { * Put a value in the JSONArray, where the value will be a JSONArray which * is produced from a Collection. * - * @param index - * The subscript. - * @param value - * A Collection value. + * @param index The subscript. + * @param value A Collection value. * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. + * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, Collection value) throws JSONException { return this.put(index, new JSONArray(value)); @@ -1033,13 +973,10 @@ public class JSONArray implements Iterable { * the JSONArray, then null elements will be added as necessary to pad it * out. * - * @param index - * The subscript. - * @param value - * A double value. + * @param index The subscript. + * @param value A double value. * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. + * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, double value) throws JSONException { return this.put(index, Double.valueOf(value)); @@ -1050,13 +987,10 @@ public class JSONArray implements Iterable { * the JSONArray, then null elements will be added as necessary to pad it * out. * - * @param index - * The subscript. - * @param value - * A float value. + * @param index The subscript. + * @param value A float value. * @return this. - * @throws JSONException - * If the index is negative or if the value is non-finite. + * @throws JSONException If the index is negative or if the value is non-finite. */ public JSONArray put(int index, float value) throws JSONException { return this.put(index, Float.valueOf(value)); @@ -1067,13 +1001,10 @@ public class JSONArray implements Iterable { * the JSONArray, then null elements will be added as necessary to pad it * out. * - * @param index - * The subscript. - * @param value - * An int value. + * @param index The subscript. + * @param value An int value. * @return this. - * @throws JSONException - * If the index is negative. + * @throws JSONException If the index is negative. */ public JSONArray put(int index, int value) throws JSONException { return this.put(index, Integer.valueOf(value)); @@ -1084,13 +1015,10 @@ public class JSONArray implements Iterable { * the JSONArray, then null elements will be added as necessary to pad it * out. * - * @param index - * The subscript. - * @param value - * A long value. + * @param index The subscript. + * @param value A long value. * @return this. - * @throws JSONException - * If the index is negative. + * @throws JSONException If the index is negative. */ public JSONArray put(int index, long value) throws JSONException { return this.put(index, Long.valueOf(value)); @@ -1100,16 +1028,12 @@ public class JSONArray implements Iterable { * Put a value in the JSONArray, where the value will be a JSONObject that * is produced from a Map. * - * @param index - * The subscript. - * @param value - * The Map value. + * @param index The subscript. + * @param value The Map value. * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. - * @throws NullPointerException - * If a key in the map is null + * @throws JSONException If the index is negative or if the the value is an invalid + * number. + * @throws NullPointerException If a key in the map is null */ public JSONArray put(int index, Map value) throws JSONException { this.put(index, new JSONObject(value)); @@ -1121,16 +1045,13 @@ public class JSONArray implements Iterable { * than the length of the JSONArray, then null elements will be added as * necessary to pad it out. * - * @param index - * The subscript. - * @param value - * The value to put into the array. The value should be a - * Boolean, Double, Integer, JSONArray, JSONObject, Long, or - * String, or the JSONObject.NULL object. + * @param index The subscript. + * @param value The value to put into the array. The value should be a + * Boolean, Double, Integer, JSONArray, JSONObject, Long, or + * String, or the JSONObject.NULL object. * @return this. - * @throws JSONException - * If the index is negative or if the the value is an invalid - * number. + * @throws JSONException If the index is negative or if the the value is an invalid + * number. */ public JSONArray put(int index, Object value) throws JSONException { if (index < 0) { @@ -1141,7 +1062,7 @@ public class JSONArray implements Iterable { this.myArrayList.set(index, value); return this; } - if(index == this.length()){ + if (index == this.length()) { // simple append return this.put(value); } @@ -1154,9 +1075,9 @@ public class JSONArray implements Iterable { } return this.put(value); } - + /** - * Creates a JSONPointer using an initialization string and tries to + * Creates a JSONPointer using an initialization string and tries to * match it to an item within this JSONArray. For example, given a * JSONArray initialized with this document: *
@@ -1164,7 +1085,7 @@ public class JSONArray implements Iterable {
      *     {"b":"c"}
      * ]
      * 
-     * and this JSONPointer string: 
+     * and this JSONPointer string:
      * 
      * "/0/b"
      * 
@@ -1177,9 +1098,9 @@ public class JSONArray implements Iterable { public Object query(String jsonPointer) { return query(new JSONPointer(jsonPointer)); } - + /** - * Uses a user initialized JSONPointer and tries to + * Uses a user initialized JSONPointer and tries to * match it to an item within this JSONArray. For example, given a * JSONArray initialized with this document: *
@@ -1187,7 +1108,7 @@ public class JSONArray implements Iterable {
      *     {"b":"c"}
      * ]
      * 
-     * and this JSONPointer: 
+     * and this JSONPointer:
      * 
      * "/0/b"
      * 
@@ -1200,23 +1121,23 @@ public class JSONArray implements Iterable { public Object query(JSONPointer jsonPointer) { return jsonPointer.queryFrom(this); } - + /** * Queries and returns a value from this object using {@code jsonPointer}, or * returns null if the query fails due to a missing key. - * + * * @param jsonPointer the string representation of the JSON pointer * @return the queried value or {@code null} * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax */ public Object optQuery(String jsonPointer) { - return optQuery(new JSONPointer(jsonPointer)); + return optQuery(new JSONPointer(jsonPointer)); } - + /** * Queries and returns a value from this object using {@code jsonPointer}, or * returns null if the query fails due to a missing key. - * + * * @param jsonPointer The JSON pointer * @return the queried value or {@code null} * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax @@ -1232,15 +1153,14 @@ public class JSONArray implements Iterable { /** * Remove an index and close the hole. * - * @param index - * The index of the element to be removed. + * @param index The index of the element to be removed. * @return The value that was associated with the index, or null if there - * was no value. + * was no value. */ public Object remove(int index) { return index >= 0 && index < this.length() - ? this.myArrayList.remove(index) - : null; + ? this.myArrayList.remove(index) + : null; } /** @@ -1255,24 +1175,24 @@ public class JSONArray implements Iterable { return false; } int len = this.length(); - if (len != ((JSONArray)other).length()) { + if (len != ((JSONArray) other).length()) { return false; } for (int i = 0; i < len; i += 1) { Object valueThis = this.myArrayList.get(i); - Object valueOther = ((JSONArray)other).myArrayList.get(i); - if(valueThis == valueOther) { - continue; + Object valueOther = ((JSONArray) other).myArrayList.get(i); + if (valueThis == valueOther) { + continue; } - if(valueThis == null) { - return false; + if (valueThis == null) { + return false; } if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { + if (!((JSONObject) valueThis).similar(valueOther)) { return false; } } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { + if (!((JSONArray) valueThis).similar(valueOther)) { return false; } } else if (!valueThis.equals(valueOther)) { @@ -1286,13 +1206,11 @@ public class JSONArray implements Iterable { * Produce a JSONObject by combining a JSONArray of names with the values of * this JSONArray. * - * @param names - * A JSONArray containing a list of key strings. These will be - * paired with the values. + * @param names A JSONArray containing a list of key strings. These will be + * paired with the values. * @return A JSONObject, or null if there are no names or if this JSONArray - * has no values. - * @throws JSONException - * If any of the names are null. + * has no values. + * @throws JSONException If any of the names are null. */ public JSONObject toJSONObject(JSONArray names) throws JSONException { if (names == null || names.isEmpty() || this.isEmpty()) { @@ -1315,7 +1233,7 @@ public class JSONArray implements Iterable { * * * @return a printable, displayable, transmittable representation of the - * array. + * array. */ @Override public String toString() { @@ -1328,11 +1246,11 @@ public class JSONArray implements Iterable { /** * Make a pretty-printed JSON text of this JSONArray. - * + * *

If indentFactor > 0 and the {@link JSONArray} has only * one element, then the array will be output on a single line: *

{@code [1]}
- * + * *

If an array has 2 or more elements, then it will be output across * multiple lines:

{@code
      * [
@@ -1344,13 +1262,12 @@ public class JSONArray implements Iterable {
      * 

* Warning: This method assumes that the data structure is acyclical. * - * - * @param indentFactor - * The number of spaces to add to each level of indentation. + * + * @param indentFactor The number of spaces to add to each level of indentation. * @return a printable, displayable, transmittable representation of the - * object, beginning with [ (left - * bracket) and ending with ] - *  (right bracket). + * object, beginning with [ (left + * bracket) and ending with ] + *  (right bracket). * @throws JSONException */ public String toString(int indentFactor) throws JSONException { @@ -1365,7 +1282,7 @@ public class JSONArray implements Iterable { * compactness, no whitespace is added. *

* Warning: This method assumes that the data structure is acyclical. - * + * * * @return The writer. * @throws JSONException @@ -1376,11 +1293,11 @@ public class JSONArray implements Iterable { /** * Write the contents of the JSONArray as JSON text to a writer. - * + * *

If indentFactor > 0 and the {@link JSONArray} has only * one element, then the array will be output on a single line: *

{@code [1]}
- * + * *

If an array has 2 or more elements, then it will be output across * multiple lines:

{@code
      * [
@@ -1393,12 +1310,9 @@ public class JSONArray implements Iterable {
      * Warning: This method assumes that the data structure is acyclical.
      * 
      *
-     * @param writer
-     *            Writes the serialized JSON
-     * @param indentFactor
-     *            The number of spaces to add to each level of indentation.
-     * @param indent
-     *            The indentation of the top level.
+     * @param writer       Writes the serialized JSON
+     * @param indentFactor The number of spaces to add to each level of indentation.
+     * @param indent       The indentation of the top level.
      * @return The writer.
      * @throws JSONException
      */
@@ -1480,38 +1394,5 @@ public class JSONArray implements Iterable {
     public boolean isEmpty() {
         return this.myArrayList.isEmpty();
     }
-    
-    /**
-     * Create a new JSONException in a common format for incorrect conversions.
-     * @param idx index of the item
-     * @param valueType the type of value being coerced to
-     * @param cause optional cause of the coercion failure
-     * @return JSONException that can be thrown.
-     */
-    private static JSONException wrongValueFormatException(
-            int idx,
-            String valueType,
-            Throwable cause) {
-        return new JSONException(
-                "JSONArray[" + idx + "] is not a " + valueType + "."
-                , cause);
-    }
-    
-    /**
-     * Create a new JSONException in a common format for incorrect conversions.
-     * @param idx index of the item
-     * @param valueType the type of value being coerced to
-     * @param cause optional cause of the coercion failure
-     * @return JSONException that can be thrown.
-     */
-    private static JSONException wrongValueFormatException(
-            int idx,
-            String valueType,
-            Object value,
-            Throwable cause) {
-        return new JSONException(
-                "JSONArray[" + idx + "] is not a " + valueType + " (" + value + ")."
-                , cause);
-    }
 
 }
diff --git a/src/main/java/json/JSONException.java b/src/main/java/json/JSONException.java
index 60998ff..ea763f6 100644
--- a/src/main/java/json/JSONException.java
+++ b/src/main/java/json/JSONException.java
@@ -7,14 +7,15 @@ package json;
  * @version 2015-12-09
  */
 public class JSONException extends RuntimeException {
-    /** Serialization ID */
+    /**
+     * Serialization ID
+     */
     private static final long serialVersionUID = 0;
 
     /**
      * Constructs a JSONException with an explanatory message.
      *
-     * @param message
-     *            Detail about the reason for the exception.
+     * @param message Detail about the reason for the exception.
      */
     public JSONException(final String message) {
         super(message);
@@ -22,11 +23,9 @@ public class JSONException extends RuntimeException {
 
     /**
      * Constructs a JSONException with an explanatory message and cause.
-     * 
-     * @param message
-     *            Detail about the reason for the exception.
-     * @param cause
-     *            The cause.
+     *
+     * @param message Detail about the reason for the exception.
+     * @param cause   The cause.
      */
     public JSONException(final String message, final Throwable cause) {
         super(message, cause);
@@ -34,9 +33,8 @@ public class JSONException extends RuntimeException {
 
     /**
      * Constructs a new JSONException with the specified cause.
-     * 
-     * @param cause
-     *            The cause.
+     *
+     * @param cause The cause.
      */
     public JSONException(final Throwable cause) {
         super(cause.getMessage(), cause);
diff --git a/src/main/java/json/JSONObject.java b/src/main/java/json/JSONObject.java
index 300d9fa..9a07ac9 100644
--- a/src/main/java/json/JSONObject.java
+++ b/src/main/java/json/JSONObject.java
@@ -70,7 +70,7 @@ import java.util.regex.Pattern;
  * myString = new JSONObject()
  *         .put("JSON", "Hello, World!").toString();
  * 
- *
+ * 

* produces the string {"JSON": "Hello, World"}. *

* The texts produced by the toString methods strictly conform to @@ -94,59 +94,15 @@ import java.util.regex.Pattern; */ public class JSONObject { /** - * JSONObject.NULL is equivalent to the value that JavaScript calls null, - * whilst Java's null is equivalent to the value that JavaScript calls - * undefined. + * It is sometimes more convenient and less ambiguous to have a + * NULL object than to use Java's null value. + * JSONObject.NULL.equals(null) returns true. + * JSONObject.NULL.toString() returns "null". */ - private static final class Null { - - /** - * There is only intended to be a single instance of the NULL object, - * so the clone method returns itself. - * - * @return NULL. - */ - @Override - protected final Object clone() { - return this; - } - - /** - * A Null object is equal to the null value and to itself. - * - * @param object - * An object to test for nullness. - * @return true if the object parameter is the JSONObject.NULL object or - * null. - */ - @Override - public boolean equals(Object object) { - return object == null || object == this; - } - /** - * A Null object is equal to the null value and to itself. - * - * @return always returns 0. - */ - @Override - public int hashCode() { - return 0; - } - - /** - * Get the "null" string value. - * - * @return The string "null". - */ - @Override - public String toString() { - return "null"; - } - } - + public static final Object NULL = new Null(); /** - * Regular Expression Pattern that matches JSON Numbers. This is primarily used for - * output to guarantee that we are always writing valid JSON. + * Regular Expression Pattern that matches JSON Numbers. This is primarily used for + * output to guarantee that we are always writing valid JSON. */ static final Pattern NUMBER_PATTERN = Pattern.compile("-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][+-]?\\d+)?"); @@ -155,22 +111,14 @@ public class JSONObject { */ private final Map map; - /** - * It is sometimes more convenient and less ambiguous to have a - * NULL object than to use Java's null value. - * JSONObject.NULL.equals(null) returns true. - * JSONObject.NULL.toString() returns "null". - */ - public static final Object NULL = new Null(); - /** * Construct an empty JSONObject. */ public JSONObject() { - // HashMap is used on purpose to ensure that elements are unordered by + // HashMap is used on purpose to ensure that elements are unordered by // the specification. - // JSON tends to be a portable transfer format to allows the container - // implementations to rearrange their items for a faster element + // JSON tends to be a portable transfer format to allows the container + // implementations to rearrange their items for a faster element // retrieval based on associative access. // Therefore, an implementation mustn't rely on the order of the item. this.map = new HashMap(); @@ -181,12 +129,10 @@ public class JSONObject { * strings is used to identify the keys that should be copied. Missing keys * are ignored. * - * @param jo - * A JSONObject. - * @param names - * An array of strings. + * @param jo A JSONObject. + * @param names An array of strings. */ - public JSONObject(JSONObject jo, String ... names) { + public JSONObject(JSONObject jo, String... names) { this(names.length); for (int i = 0; i < names.length; i += 1) { try { @@ -199,11 +145,9 @@ public class JSONObject { /** * Construct a JSONObject from a JSONTokener. * - * @param x - * A JSONTokener object containing the source string. - * @throws JSONException - * If there is a syntax error in the source string or a - * duplicated key. + * @param x A JSONTokener object containing the source string. + * @throws JSONException If there is a syntax error in the source string or a + * duplicated key. */ public JSONObject(JSONTokener x) throws JSONException { this(); @@ -213,16 +157,16 @@ public class JSONObject { if (x.nextClean() != '{') { throw x.syntaxError("A JSONObject text must begin with '{'"); } - for (;;) { + for (; ; ) { c = x.nextClean(); switch (c) { - case 0: - throw x.syntaxError("A JSONObject text must end with '}'"); - case '}': - return; - default: - x.back(); - key = x.nextValue().toString(); + case 0: + throw x.syntaxError("A JSONObject text must end with '}'"); + case '}': + return; + default: + x.back(); + key = x.nextValue().toString(); } // The key is followed by ':'. @@ -231,9 +175,9 @@ public class JSONObject { if (c != ':') { throw x.syntaxError("Expected a ':' after a key"); } - + // Use syntaxError(..) to include error location - + if (key != null) { // Check if key exists if (this.opt(key) != null) { @@ -242,7 +186,7 @@ public class JSONObject { } // Only add value if non-null Object value = x.nextValue(); - if (value!=null) { + if (value != null) { this.put(key, value); } } @@ -250,17 +194,17 @@ public class JSONObject { // Pairs are separated by ','. switch (x.nextClean()) { - case ';': - case ',': - if (x.nextClean() == '}') { + case ';': + case ',': + if (x.nextClean() == '}') { + return; + } + x.back(); + break; + case '}': return; - } - x.back(); - break; - case '}': - return; - default: - throw x.syntaxError("Expected a ',' or '}'"); + default: + throw x.syntaxError("Expected a ',' or '}'"); } } } @@ -268,23 +212,20 @@ public class JSONObject { /** * Construct a JSONObject from a Map. * - * @param m - * A map object that can be used to initialize the contents of - * the JSONObject. - * @throws JSONException - * If a value in the map is non-finite number. - * @throws NullPointerException - * If a key in the map is null + * @param m A map object that can be used to initialize the contents of + * the JSONObject. + * @throws JSONException If a value in the map is non-finite number. + * @throws NullPointerException If a key in the map is null */ public JSONObject(Map m) { if (m == null) { this.map = new HashMap(); } else { this.map = new HashMap(m.size()); - for (final Entry e : m.entrySet()) { - if(e.getKey() == null) { - throw new NullPointerException("Null key."); - } + for (final Entry e : m.entrySet()) { + if (e.getKey() == null) { + throw new NullPointerException("Null key."); + } final Object value = e.getValue(); if (value != null) { this.map.put(String.valueOf(e.getKey()), wrap(value)); @@ -342,14 +283,13 @@ public class JSONObject { * method from being serialized: *

      * @JSONPropertyName("FullName")
-     * @JSONPropertyIgnore 
+     * @JSONPropertyIgnore
      * public String getName() { return this.name; }
      * 
*

- * - * @param bean - * An object that has getter methods that should be used to make - * a JSONObject. + * + * @param bean An object that has getter methods that should be used to make + * a JSONObject. */ public JSONObject(Object bean) { this(); @@ -363,14 +303,12 @@ public class JSONObject { * those keys in the object. If a key is not found or not visible, then it * will not be copied into the new JSONObject. * - * @param object - * An object that has fields that should be used to make a - * JSONObject. - * @param names - * An array of strings, the names of the fields to be obtained - * from the object. + * @param object An object that has fields that should be used to make a + * JSONObject. + * @param names An array of strings, the names of the fields to be obtained + * from the object. */ - public JSONObject(Object object, String ... names) { + public JSONObject(Object object, String... names) { this(names.length); Class c = object.getClass(); for (int i = 0; i < names.length; i += 1) { @@ -386,13 +324,11 @@ public class JSONObject { * Construct a JSONObject from a source JSON text string. This is the most * commonly used JSONObject constructor. * - * @param source - * A string beginning with { (left - * brace) and ending with } - *  (right brace). - * @exception JSONException - * If there is a syntax error in the source string or a - * duplicated key. + * @param source A string beginning with { (left + * brace) and ending with } + *  (right brace). + * @throws JSONException If there is a syntax error in the source string or a + * duplicated key. */ public JSONObject(String source) throws JSONException { this(new JSONTokener(source)); @@ -401,12 +337,9 @@ public class JSONObject { /** * Construct a JSONObject from a ResourceBundle. * - * @param baseName - * The ResourceBundle base name. - * @param locale - * The Locale to load the ResourceBundle for. - * @throws JSONException - * If any JSONExceptions are detected. + * @param baseName The ResourceBundle base name. + * @param locale The Locale to load the ResourceBundle for. + * @throws JSONException If any JSONExceptions are detected. */ public JSONObject(String baseName, Locale locale) throws JSONException { this(); @@ -440,90 +373,23 @@ public class JSONObject { } } } - + /** - * Constructor to specify an initial capacity of the internal map. Useful for library + * Constructor to specify an initial capacity of the internal map. Useful for library * internal calls where we know, or at least can best guess, how big this JSONObject * will be. - * + * * @param initialCapacity initial capacity of the internal map. */ - protected JSONObject(int initialCapacity){ + protected JSONObject(int initialCapacity) { this.map = new HashMap(initialCapacity); } - /** - * Accumulate values under a key. It is similar to the put method except - * that if there is already an object stored under the key then a JSONArray - * is stored under the key to hold all of the accumulated values. If there - * is already a JSONArray, then the new value is appended to it. In - * contrast, the put method replaces the previous value. - * - * If only one value is accumulated that is not a JSONArray, then the result - * will be the same as using put. But if multiple values are accumulated, - * then the result will be like append. - * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject accumulate(String key, Object value) throws JSONException { - testValidity(value); - Object object = this.opt(key); - if (object == null) { - this.put(key, - value instanceof JSONArray ? new JSONArray().put(value) - : value); - } else if (object instanceof JSONArray) { - ((JSONArray) object).put(value); - } else { - this.put(key, new JSONArray().put(object).put(value)); - } - return this; - } - - /** - * Append values to the array under a key. If the key does not exist in the - * JSONObject, then the key is put in the JSONObject with its value being a - * JSONArray containing the value parameter. If the key was already - * associated with a JSONArray, then the value parameter is appended to it. - * - * @param key - * A key string. - * @param value - * An object to be accumulated under the key. - * @return this. - * @throws JSONException - * If the value is non-finite number or if the current value associated with - * the key is not a JSONArray. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject append(String key, Object value) throws JSONException { - testValidity(value); - Object object = this.opt(key); - if (object == null) { - this.put(key, new JSONArray().put(value)); - } else if (object instanceof JSONArray) { - this.put(key, ((JSONArray) object).put(value)); - } else { - throw wrongValueFormatException(key, "JSONArray", null, null); - } - return this; - } - /** * Produce a string from a double. The string "null" will be returned if the * number is not finite. * - * @param d - * A double. + * @param d A double. * @return A String. */ public static String doubleToString(double d) { @@ -546,275 +412,10 @@ public class JSONObject { return string; } - /** - * Get the value object associated with a key. - * - * @param key - * A key string. - * @return The object associated with the key. - * @throws JSONException - * if the key is not found. - */ - public Object get(String key) throws JSONException { - if (key == null) { - throw new JSONException("Null key."); - } - Object object = this.opt(key); - if (object == null) { - throw new JSONException("JSONObject[" + quote(key) + "] not found."); - } - return object; - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @return The enum value associated with the key - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an enum. - */ - public > E getEnum(Class clazz, String key) throws JSONException { - E val = optEnum(clazz, key); - if(val==null) { - // JSONException should really take a throwable argument. - // If it did, I would re-implement this with the Enum.valueOf - // method and place any thrown exception in the JSONException - throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), null); - } - return val; - } - - /** - * Get the boolean value associated with a key. - * - * @param key - * A key string. - * @return The truth. - * @throws JSONException - * if the value is not a Boolean or the String "true" or - * "false". - */ - public boolean getBoolean(String key) throws JSONException { - Object object = this.get(key); - if (object.equals(Boolean.FALSE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("false"))) { - return false; - } else if (object.equals(Boolean.TRUE) - || (object instanceof String && ((String) object) - .equalsIgnoreCase("true"))) { - return true; - } - throw wrongValueFormatException(key, "Boolean", null); - } - - /** - * Get the BigInteger value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value cannot - * be converted to BigInteger. - */ - public BigInteger getBigInteger(String key) throws JSONException { - Object object = this.get(key); - BigInteger ret = objectToBigInteger(object, null); - if (ret != null) { - return ret; - } - throw wrongValueFormatException(key, "BigInteger", object, null); - } - - /** - * Get the BigDecimal value associated with a key. If the value is float or - * double, the the {@link BigDecimal#BigDecimal(double)} constructor will - * be used. See notes on the constructor for conversion issues that may - * arise. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value - * cannot be converted to BigDecimal. - */ - public BigDecimal getBigDecimal(String key) throws JSONException { - Object object = this.get(key); - BigDecimal ret = objectToBigDecimal(object, null); - if (ret != null) { - return ret; - } - throw wrongValueFormatException(key, "BigDecimal", object, null); - } - - /** - * Get the double value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public double getDouble(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).doubleValue(); - } - try { - return Double.parseDouble(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "double", e); - } - } - - /** - * Get the float value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public float getFloat(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).floatValue(); - } - try { - return Float.parseFloat(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "float", e); - } - } - - /** - * Get the Number value associated with a key. - * - * @param key - * A key string. - * @return The numeric value. - * @throws JSONException - * if the key is not found or if the value is not a Number - * object and cannot be converted to a number. - */ - public Number getNumber(String key) throws JSONException { - Object object = this.get(key); - try { - if (object instanceof Number) { - return (Number)object; - } - return stringToNumber(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "number", e); - } - } - - /** - * Get the int value associated with a key. - * - * @param key - * A key string. - * @return The integer value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to an integer. - */ - public int getInt(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).intValue(); - } - try { - return Integer.parseInt(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "int", e); - } - } - - /** - * Get the JSONArray value associated with a key. - * - * @param key - * A key string. - * @return A JSONArray which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONArray. - */ - public JSONArray getJSONArray(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof JSONArray) { - return (JSONArray) object; - } - throw wrongValueFormatException(key, "JSONArray", null); - } - - /** - * Get the JSONObject value associated with a key. - * - * @param key - * A key string. - * @return A JSONObject which is the value. - * @throws JSONException - * if the key is not found or if the value is not a JSONObject. - */ - public JSONObject getJSONObject(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof JSONObject) { - return (JSONObject) object; - } - throw wrongValueFormatException(key, "JSONObject", null); - } - - /** - * Get the long value associated with a key. - * - * @param key - * A key string. - * @return The long value. - * @throws JSONException - * if the key is not found or if the value cannot be converted - * to a long. - */ - public long getLong(String key) throws JSONException { - final Object object = this.get(key); - if(object instanceof Number) { - return ((Number)object).longValue(); - } - try { - return Long.parseLong(object.toString()); - } catch (Exception e) { - throw wrongValueFormatException(key, "long", e); - } - } - - public UUID getUUID(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof UUID) { - return (UUID) object; - } else if (object instanceof String) { - return UUID.fromString(String.valueOf(object)); - } - throw wrongValueFormatException(key, "UUID", null); - } - /** * Get an array of field names from a JSONObject. * - * @param jo - * JSON object + * @param jo JSON object * @return An array of field names, or null if there are no names. */ public static String[] getNames(JSONObject jo) { @@ -827,8 +428,7 @@ public class JSONObject { /** * Get an array of public field names from an Object. * - * @param object - * object to read + * @param object object to read * @return An array of field names, or null if there are no names. */ public static String[] getNames(Object object) { @@ -848,188 +448,12 @@ public class JSONObject { return names; } - /** - * Get the string associated with a key. - * - * @param key - * A key string. - * @return A string which is the value. - * @throws JSONException - * if there is no string value for the key. - */ - public String getString(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof String) { - return (String) object; - } - throw wrongValueFormatException(key, "string", null); - } - - public byte getByte(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof Byte) { - return (byte) object; - } - throw wrongValueFormatException(key, "byte", null); - } - - public byte[] getByteArray(String key) throws JSONException { - Object object = this.get(key); - if (object instanceof JSONArray) { - JSONArray jsonArray = (JSONArray) object; - byte[] array = new byte[jsonArray.length()]; - for (int k = 0; k < jsonArray.length(); k++) { - array[k] = Byte.valueOf(String.valueOf(jsonArray.get(k))); - } - return array; - } - if (object instanceof byte[]) { - return (byte[]) object; - } - throw wrongValueFormatException(key, "byteArray", null); - } - - /** - * Determine if the JSONObject contains a specific key. - * - * @param key - * A key string. - * @return true if the key exists in the JSONObject. - */ - public boolean has(String key) { - return this.map.containsKey(key); - } - - /** - * Increment a property of a JSONObject. If there is no such property, - * create one with a value of 1 (Integer). If there is such a property, and if it is - * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it. - * No overflow bounds checking is performed, so callers should initialize the key - * prior to this call with an appropriate type that can handle the maximum expected - * value. - * - * @param key - * A key string. - * @return this. - * @throws JSONException - * If there is already a property with this name that is not an - * Integer, Long, Double, or Float. - */ - public JSONObject increment(String key) throws JSONException { - Object value = this.opt(key); - if (value == null) { - this.put(key, 1); - } else if (value instanceof Integer) { - this.put(key, ((Integer) value).intValue() + 1); - } else if (value instanceof Long) { - this.put(key, ((Long) value).longValue() + 1L); - } else if (value instanceof BigInteger) { - this.put(key, ((BigInteger)value).add(BigInteger.ONE)); - } else if (value instanceof Float) { - this.put(key, ((Float) value).floatValue() + 1.0f); - } else if (value instanceof Double) { - this.put(key, ((Double) value).doubleValue() + 1.0d); - } else if (value instanceof BigDecimal) { - this.put(key, ((BigDecimal)value).add(BigDecimal.ONE)); - } else { - throw new JSONException("Unable to increment [" + quote(key) + "]."); - } - return this; - } - - /** - * Determine if the value associated with the key is null or if there is no - * value. - * - * @param key - * A key string. - * @return true if there is no value associated with the key or if the value - * is the JSONObject.NULL object. - */ - public boolean isNull(String key) { - return JSONObject.NULL.equals(this.opt(key)); - } - - /** - * Get an enumeration of the keys of the JSONObject. Modifying this key Set will also - * modify the JSONObject. Use with caution. - * - * @see Set#iterator() - * - * @return An iterator of the keys. - */ - public Iterator keys() { - return this.keySet().iterator(); - } - - /** - * Get a set of keys of the JSONObject. Modifying this key Set will also modify the - * JSONObject. Use with caution. - * - * @see Map#keySet() - * - * @return A keySet. - */ - public Set keySet() { - return this.map.keySet(); - } - - /** - * Get a set of entries of the JSONObject. These are raw values and may not - * match what is returned by the JSONObject get* and opt* functions. Modifying - * the returned EntrySet or the Entry objects contained therein will modify the - * backing JSONObject. This does not return a clone or a read-only view. - * - * Use with caution. - * - * @see Map#entrySet() - * - * @return An Entry Set - */ - protected Set> entrySet() { - return this.map.entrySet(); - } - - /** - * Get the number of keys stored in the JSONObject. - * - * @return The number of keys in the JSONObject. - */ - public int length() { - return this.map.size(); - } - - /** - * Check if JSONObject is empty. - * - * @return true if JSONObject is empty, otherwise false. - */ - public boolean isEmpty() { - return this.map.isEmpty(); - } - - /** - * Produce a JSONArray containing the names of the elements of this - * JSONObject. - * - * @return A JSONArray containing the key strings, or null if the JSONObject - * is empty. - */ - public JSONArray names() { - if(this.map.isEmpty()) { - return null; - } - return new JSONArray(this.map.keySet()); - } - /** * Produce a string from a Number. * - * @param number - * A Number + * @param number A Number * @return A String. - * @throws JSONException - * If n is a non-finite number. + * @throws JSONException If n is a non-finite number. */ public static String numberToString(Number number) throws JSONException { if (number == null) { @@ -1053,148 +477,30 @@ public class JSONObject { } /** - * Get an optional value associated with a key. - * - * @param key - * A key string. - * @return An object which is the value, or null if there is no value. - */ - public Object opt(String key) { - return key == null ? null : this.map.get(key); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @return The enum value associated with the key or null if not found - */ - public > E optEnum(Class clazz, String key) { - return this.optEnum(clazz, key, null); - } - - /** - * Get the enum value associated with a key. - * - * @param - * Enum Type - * @param clazz - * The type of enum to retrieve. - * @param key - * A key string. - * @param defaultValue - * The default in case the value is not found - * @return The enum value associated with the key or defaultValue - * if the value is not found or cannot be assigned to clazz - */ - public > E optEnum(Class clazz, String key, E defaultValue) { - try { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (clazz.isAssignableFrom(val.getClass())) { - // we just checked it! - @SuppressWarnings("unchecked") - E myE = (E) val; - return myE; - } - return Enum.valueOf(clazz, val.toString()); - } catch (IllegalArgumentException e) { - return defaultValue; - } catch (NullPointerException e) { - return defaultValue; - } - } - - /** - * Get an optional boolean associated with a key. It returns false if there - * is no such key, or if the value is not Boolean.TRUE or the String "true". - * - * @param key - * A key string. - * @return The truth. - */ - public boolean optBoolean(String key) { - return this.optBoolean(key, false); - } - - /** - * Get an optional boolean associated with a key. It returns the - * defaultValue if there is no such key, or if it is not a Boolean or the - * String "true" or "false" (case insensitive). - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return The truth. - */ - public boolean optBoolean(String key, boolean defaultValue) { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof Boolean){ - return ((Boolean) val).booleanValue(); - } - try { - // we'll use the get anyway because it does string conversion. - return this.getBoolean(key); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional BigDecimal associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. If the value - * is float or double, then the {@link BigDecimal#BigDecimal(double)} - * constructor will be used. See notes on the constructor for conversion - * issues that may arise. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { - Object val = this.opt(key); - return objectToBigDecimal(val, defaultValue); - } - - /** - * @param val value to convert + * @param val value to convert * @param defaultValue default value to return is the conversion doesn't work or is null. * @return BigDecimal conversion of the original value, or the defaultValue if unable - * to convert. + * to convert. */ static BigDecimal objectToBigDecimal(Object val, BigDecimal defaultValue) { if (NULL.equals(val)) { return defaultValue; } - if (val instanceof BigDecimal){ + if (val instanceof BigDecimal) { return (BigDecimal) val; } - if (val instanceof BigInteger){ + if (val instanceof BigInteger) { return new BigDecimal((BigInteger) val); } - if (val instanceof Double || val instanceof Float){ + if (val instanceof Double || val instanceof Float) { final double d = ((Number) val).doubleValue(); - if(Double.isNaN(d)) { + if (Double.isNaN(d)) { return defaultValue; } return new BigDecimal(((Number) val).doubleValue()); } if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ + || val instanceof Short || val instanceof Byte) { return new BigDecimal(((Number) val).longValue()); } // don't check if it's a string in case of unchecked Number subclasses @@ -1206,57 +512,41 @@ public class JSONObject { } /** - * Get an optional BigInteger associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public BigInteger optBigInteger(String key, BigInteger defaultValue) { - Object val = this.opt(key); - return objectToBigInteger(val, defaultValue); - } - - /** - * @param val value to convert + * @param val value to convert * @param defaultValue default value to return is the conversion doesn't work or is null. * @return BigInteger conversion of the original value, or the defaultValue if unable - * to convert. + * to convert. */ static BigInteger objectToBigInteger(Object val, BigInteger defaultValue) { if (NULL.equals(val)) { return defaultValue; } - if (val instanceof BigInteger){ + if (val instanceof BigInteger) { return (BigInteger) val; } - if (val instanceof BigDecimal){ + if (val instanceof BigDecimal) { return ((BigDecimal) val).toBigInteger(); } - if (val instanceof Double || val instanceof Float){ + if (val instanceof Double || val instanceof Float) { final double d = ((Number) val).doubleValue(); - if(Double.isNaN(d)) { + if (Double.isNaN(d)) { return defaultValue; } return new BigDecimal(d).toBigInteger(); } if (val instanceof Long || val instanceof Integer - || val instanceof Short || val instanceof Byte){ + || val instanceof Short || val instanceof Byte) { return BigInteger.valueOf(((Number) val).longValue()); } // don't check if it's a string in case of unchecked Number subclasses try { - // the other opt functions handle implicit conversions, i.e. + // the other opt functions handle implicit conversions, i.e. // jo.put("double",1.1d); // jo.optInt("double"); -- will return 1, not an error // this conversion to BigDecimal then to BigInteger is to maintain // that type cast support that may truncate the decimal. final String valStr = val.toString(); - if(isDecimalNotation(valStr)) { + if (isDecimalNotation(valStr)) { return new BigDecimal(valStr).toBigInteger(); } return new BigInteger(valStr); @@ -1265,289 +555,6 @@ public class JSONObject { } } - /** - * Get an optional double associated with a key, or NaN if there is no such - * key or if its value is not a number. If the value is a string, an attempt - * will be made to evaluate it as a number. - * - * @param key - * A string which is the key. - * @return An object which is the value. - */ - public double optDouble(String key) { - return this.optDouble(key, Double.NaN); - } - - /** - * Get an optional double associated with a key, or the defaultValue if - * there is no such key or if its value is not a number. If the value is a - * string, an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public double optDouble(String key, double defaultValue) { - Number val = this.optNumber(key); - if (val == null) { - return defaultValue; - } - final double doubleValue = val.doubleValue(); - // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { - // return defaultValue; - // } - return doubleValue; - } - - /** - * Get the optional double value associated with an index. NaN is returned - * if there is no value for the index, or if the value is not a number and - * cannot be converted to a number. - * - * @param key - * A key string. - * @return The value. - */ - public float optFloat(String key) { - return this.optFloat(key, Float.NaN); - } - - /** - * Get the optional double value associated with an index. The defaultValue - * is returned if there is no value for the index, or if the value is not a - * number and cannot be converted to a number. - * - * @param key - * A key string. - * @param defaultValue - * The default value. - * @return The value. - */ - public float optFloat(String key, float defaultValue) { - Number val = this.optNumber(key); - if (val == null) { - return defaultValue; - } - final float floatValue = val.floatValue(); - // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { - // return defaultValue; - // } - return floatValue; - } - - /** - * Get an optional int value associated with a key, or zero if there is no - * such key or if the value is not a number. If the value is a string, an - * attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public int optInt(String key) { - return this.optInt(key, 0); - } - - /** - * Get an optional int value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public int optInt(String key, int defaultValue) { - final Number val = this.optNumber(key, null); - if (val == null) { - return defaultValue; - } - return val.intValue(); - } - - /** - * Get an optional JSONArray associated with a key. It returns null if there - * is no such key, or if its value is not a JSONArray. - * - * @param key - * A key string. - * @return A JSONArray which is the value. - */ - public JSONArray optJSONArray(String key) { - Object o = this.opt(key); - return o instanceof JSONArray ? (JSONArray) o : null; - } - - /** - * Get an optional JSONObject associated with a key. It returns null if - * there is no such key, or if its value is not a JSONObject. - * - * @param key - * A key string. - * @return A JSONObject which is the value. - */ - public JSONObject optJSONObject(String key) { - Object object = this.opt(key); - return object instanceof JSONObject ? (JSONObject) object : null; - } - - /** - * Get an optional long value associated with a key, or zero if there is no - * such key or if the value is not a number. If the value is a string, an - * attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public long optLong(String key) { - return this.optLong(key, 0); - } - - /** - * Get an optional long value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public long optLong(String key, long defaultValue) { - final Number val = this.optNumber(key, null); - if (val == null) { - return defaultValue; - } - - return val.longValue(); - } - - /** - * Get an optional {@link Number} value associated with a key, or null - * if there is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param key - * A key string. - * @return An object which is the value. - */ - public Number optNumber(String key) { - return this.optNumber(key, null); - } - - /** - * Get an optional {@link Number} value associated with a key, or the default if there - * is no such key or if the value is not a number. If the value is a string, - * an attempt will be made to evaluate it as a number. This method - * would be used in cases where type coercion of the number value is unwanted. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return An object which is the value. - */ - public Number optNumber(String key, Number defaultValue) { - Object val = this.opt(key); - if (NULL.equals(val)) { - return defaultValue; - } - if (val instanceof Number){ - return (Number) val; - } - - try { - return stringToNumber(val.toString()); - } catch (Exception e) { - return defaultValue; - } - } - - /** - * Get an optional string associated with a key. It returns an empty string - * if there is no such key. If the value is not a string and is not null, - * then it is converted to a string. - * - * @param key - * A key string. - * @return A string which is the value. - */ - public String optString(String key) { - return this.optString(key, ""); - } - - /** - * Get an optional string associated with a key. It returns the defaultValue - * if there is no such key. - * - * @param key - * A key string. - * @param defaultValue - * The default. - * @return A string which is the value. - */ - public String optString(String key, String defaultValue) { - Object object = this.opt(key); - return NULL.equals(object) ? defaultValue : object.toString(); - } - - /** - * Populates the internal map of the JSONObject with the bean properties. The - * bean can not be recursive. - * - * @see JSONObject#JSONObject(Object) - * - * @param bean - * the bean - */ - private void populateMap(Object bean) { - Class klass = bean.getClass(); - - // If klass is a System class then set includeSuperClass to false. - - boolean includeSuperClass = klass.getClassLoader() != null; - - Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); - for (final Method method : methods) { - final int modifiers = method.getModifiers(); - if (Modifier.isPublic(modifiers) - && !Modifier.isStatic(modifiers) - && method.getParameterTypes().length == 0 - && !method.isBridge() - && method.getReturnType() != Void.TYPE - && isValidMethodName(method.getName())) { - final String key = getKeyNameFromMethod(method); - if (key != null && !key.isEmpty()) { - try { - final Object result = method.invoke(bean); - if (result != null) { - this.map.put(key, wrap(result)); - // we don't use the result anywhere outside of wrap - // if it's a resource we should be sure to close it - // after calling toString - if (result instanceof Closeable) { - try { - ((Closeable) result).close(); - } catch (IOException ignore) { - } - } - } - } catch (IllegalAccessException ignore) { - } catch (IllegalArgumentException ignore) { - } catch (InvocationTargetException ignore) { - } - } - } - } - } - private static boolean isValidMethodName(String name) { return !"getClass".equals(name) && !"getDeclaringClass".equals(name); } @@ -1593,15 +600,11 @@ public class JSONObject { * Searches the class hierarchy to see if the method or it's super * implementations and interfaces has the annotation. * - * @param - * type of the annotation - * - * @param m - * method to check - * @param annotationClass - * annotation to look for + * @param type of the annotation + * @param m method to check + * @param annotationClass annotation to look for * @return the {@link Annotation} if the annotation exists on the current method - * or one of it's super class definitions + * or one of it's super class definitions */ private static A getAnnotation(final Method m, final Class annotationClass) { // if we have invalid data the result is null @@ -1646,13 +649,11 @@ public class JSONObject { * Searches the class hierarchy to see if the method or it's super * implementations and interfaces has the annotation. Returns the depth of the * annotation in the hierarchy. + *

+ * type of the annotation * - * type of the annotation - * - * @param m - * method to check - * @param annotationClass - * annotation to look for + * @param m method to check + * @param annotationClass annotation to look for * @return Depth of the annotation or -1 if the annotation is not on the method. */ private static int getAnnotationDepth(final Method m, final Class annotationClass) { @@ -1703,285 +704,6 @@ public class JSONObject { } } - /** - * Put a key/boolean pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A boolean which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, boolean value) throws JSONException { - return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); - } - - public JSONObject put(String key, byte value) throws JSONException { - return this.put(key, Byte.valueOf(value)); - } - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONArray which is produced from a Collection. - * - * @param key - * A key string. - * @param value - * A Collection value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Collection value) throws JSONException { - return this.put(key, new JSONArray(value)); - } - - /** - * Put a key/double pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A double which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, double value) throws JSONException { - return this.put(key, Double.valueOf(value)); - } - - /** - * Put a key/float pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A float which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, float value) throws JSONException { - return this.put(key, Float.valueOf(value)); - } - - /** - * Put a key/int pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * An int which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, int value) throws JSONException { - return this.put(key, Integer.valueOf(value)); - } - - /** - * Put a key/long pair in the JSONObject. - * - * @param key - * A key string. - * @param value - * A long which is the value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, long value) throws JSONException { - return this.put(key, Long.valueOf(value)); - } - - /** - * Put a key/value pair in the JSONObject, where the value will be a - * JSONObject which is produced from a Map. - * - * @param key - * A key string. - * @param value - * A Map value. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Map value) throws JSONException { - return this.put(key, new JSONObject(value)); - } - - /** - * Put a key/value pair in the JSONObject. If the value is null, then the - * key will be removed from the JSONObject if it is present. - * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the value is non-finite number. - * @throws NullPointerException - * If the key is null. - */ - public JSONObject put(String key, Object value) throws JSONException { - if (key == null) { - throw new NullPointerException("Null key."); - } - if (value != null) { - testValidity(value); - this.map.put(key, value); - } else { - this.remove(key); - } - return this; - } - - public JSONObject put(String key, byte[] bytes) throws JSONException { - return this.put(key, (Object) bytes); - } - - public JSONObject put(String key, UUID uuid) throws JSONException { - return this.put(key, uuid.toString()); - } - - /** - * Put a key/value pair in the JSONObject, but only if the key and the value - * are both non-null, and only if there is not already a member with that - * name. - * - * @param key - * key to insert into - * @param value - * value to insert - * @return this. - * @throws JSONException - * if the key is a duplicate - */ - public JSONObject putOnce(String key, Object value) throws JSONException { - if (key != null && value != null) { - if (this.opt(key) != null) { - throw new JSONException("Duplicate key \"" + key + "\""); - } - return this.put(key, value); - } - return this; - } - - /** - * Put a key/value pair in the JSONObject, but only if the key and the value - * are both non-null. - * - * @param key - * A key string. - * @param value - * An object which is the value. It should be of one of these - * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, - * String, or the JSONObject.NULL object. - * @return this. - * @throws JSONException - * If the value is a non-finite number. - */ - public JSONObject putOpt(String key, Object value) throws JSONException { - if (key != null && value != null) { - return this.put(key, value); - } - return this; - } - - /** - * Creates a JSONPointer using an initialization string and tries to - * match it to an item within this JSONObject. For example, given a - * JSONObject initialized with this document: - *

-     * {
-     *     "a":{"b":"c"}
-     * }
-     * 
- * and this JSONPointer string: - *
-     * "/a/b"
-     * 
- * Then this method will return the String "c". - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(String jsonPointer) { - return query(new JSONPointer(jsonPointer)); - } - /** - * Uses a user initialized JSONPointer and tries to - * match it to an item within this JSONObject. For example, given a - * JSONObject initialized with this document: - *
-     * {
-     *     "a":{"b":"c"}
-     * }
-     * 
- * and this JSONPointer: - *
-     * "/a/b"
-     * 
- * Then this method will return the String "c". - * A JSONPointerException may be thrown from code called by this method. - * - * @param jsonPointer string that can be used to create a JSONPointer - * @return the item matched by the JSONPointer, otherwise null - */ - public Object query(JSONPointer jsonPointer) { - return jsonPointer.queryFrom(this); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer the string representation of the JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(String jsonPointer) { - return optQuery(new JSONPointer(jsonPointer)); - } - - /** - * Queries and returns a value from this object using {@code jsonPointer}, or - * returns null if the query fails due to a missing key. - * - * @param jsonPointer The JSON pointer - * @return the queried value or {@code null} - * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax - */ - public Object optQuery(JSONPointer jsonPointer) { - try { - return jsonPointer.queryFrom(this); - } catch (JSONPointerException e) { - return null; - } - } - /** * Produce a string in double quotes with backslash sequences in all the * right places. A backslash will be inserted within </, producing @@ -1989,8 +711,7 @@ public class JSONObject { * string cannot contain a control character or an unescaped quote or * backslash. * - * @param string - * A String + * @param string A String * @return A String correctly formatted for insertion in a JSON text. */ public static String quote(String string) { @@ -2022,107 +743,51 @@ public class JSONObject { b = c; c = string.charAt(i); switch (c) { - case '\\': - case '"': - w.write('\\'); - w.write(c); - break; - case '/': - if (b == '<') { + case '\\': + case '"': w.write('\\'); - } - w.write(c); - break; - case '\b': - w.write("\\b"); - break; - case '\t': - w.write("\\t"); - break; - case '\n': - w.write("\\n"); - break; - case '\f': - w.write("\\f"); - break; - case '\r': - w.write("\\r"); - break; - default: - if (c < ' ' || (c >= '\u0080' && c < '\u00a0') - || (c >= '\u2000' && c < '\u2100')) { - w.write("\\u"); - hhhh = Integer.toHexString(c); - w.write("0000", 0, 4 - hhhh.length()); - w.write(hhhh); - } else { w.write(c); - } + break; + case '/': + if (b == '<') { + w.write('\\'); + } + w.write(c); + break; + case '\b': + w.write("\\b"); + break; + case '\t': + w.write("\\t"); + break; + case '\n': + w.write("\\n"); + break; + case '\f': + w.write("\\f"); + break; + case '\r': + w.write("\\r"); + break; + default: + if (c < ' ' || (c >= '\u0080' && c < '\u00a0') + || (c >= '\u2000' && c < '\u2100')) { + w.write("\\u"); + hhhh = Integer.toHexString(c); + w.write("0000", 0, 4 - hhhh.length()); + w.write(hhhh); + } else { + w.write(c); + } } } w.write('"'); return w; } - /** - * Remove a name and its value, if present. - * - * @param key - * The name to be removed. - * @return The value that was associated with the name, or null if there was - * no value. - */ - public Object remove(String key) { - return this.map.remove(key); - } - - /** - * Determine if two JSONObjects are similar. - * They must contain the same set of names which must be associated with - * similar values. - * - * @param other The other JSONObject - * @return true if they are equal - */ - public boolean similar(Object other) { - try { - if (!(other instanceof JSONObject)) { - return false; - } - if (!this.keySet().equals(((JSONObject)other).keySet())) { - return false; - } - for (final Entry entry : this.entrySet()) { - String name = entry.getKey(); - Object valueThis = entry.getValue(); - Object valueOther = ((JSONObject)other).get(name); - if(valueThis == valueOther) { - continue; - } - if(valueThis == null) { - return false; - } - if (valueThis instanceof JSONObject) { - if (!((JSONObject)valueThis).similar(valueOther)) { - return false; - } - } else if (valueThis instanceof JSONArray) { - if (!((JSONArray)valueThis).similar(valueOther)) { - return false; - } - } else if (!valueThis.equals(valueOther)) { - return false; - } - } - return true; - } catch (Throwable exception) { - return false; - } - } - /** * Tests if the value should be tried as a decimal. It makes no test if there are actual digits. - * + * * @param val value to test * @return true if the string is "-0" or if it contains '.', 'e', or 'E', false otherwise. */ @@ -2130,16 +795,16 @@ public class JSONObject { return val.indexOf('.') > -1 || val.indexOf('e') > -1 || val.indexOf('E') > -1 || "-0".equals(val); } - + /** - * Converts a string to a number using the narrowest possible type. Possible + * Converts a string to a number using the narrowest possible type. Possible * returns for this function are BigDecimal, Double, BigInteger, Long, and Integer. * When a Double is returned, it should always be a valid Double and not NaN or +-infinity. - * + * * @param val value to convert * @return Number representation of the value. * @throws NumberFormatException thrown if the value is not a valid number. A public - * caller should catch this and wrap it in a {@link JSONException} if applicable. + * caller should catch this and wrap it in a {@link JSONException} if applicable. */ protected static Number stringToNumber(final String val) throws NumberFormatException { char initial = val.charAt(0); @@ -2148,7 +813,7 @@ public class JSONObject { if (isDecimalNotation(val)) { // quick dirty way to see if we need a BigDecimal instead of a Double // this only handles some cases of overflow or underflow - if (val.length()>14) { + if (val.length() > 14) { return new BigDecimal(val); } final Double d = Double.valueOf(val); @@ -2164,7 +829,7 @@ public class JSONObject { // integer representation. // This will narrow any values to the smallest reasonable Object representation // (Integer, Long, or BigInteger) - + // string version // The compare string length method reduces GC, // but leads to smaller integers being placed in larger wrappers even though not @@ -2177,33 +842,31 @@ public class JSONObject { // return Long.valueOf(val); //} //return new BigInteger(val); - + // BigInteger version: We use a similar bitLength compare as // BigInteger#intValueExact uses. Increases GC, but objects hold // only what they need. i.e. Less runtime overhead if the value is // long lived. Which is the better tradeoff? This is closer to what's // in stringToValue. BigInteger bi = new BigInteger(val); - if(bi.bitLength()<=31){ + if (bi.bitLength() <= 31) { return Integer.valueOf(bi.intValue()); } - if(bi.bitLength()<=63){ + if (bi.bitLength() <= 63) { return Long.valueOf(bi.longValue()); } return bi; } - throw new NumberFormatException("val ["+val+"] is not a valid number."); + throw new NumberFormatException("val [" + val + "] is not a valid number."); } /** * Try to convert a string into a number, boolean, or null. If the string * can't be converted, return the string. * - * @param string - * A String. can not be null. + * @param string A String. can not be null. * @return A simple JSON value. - * @throws NullPointerException - * Thrown if the string is null. + * @throws NullPointerException Thrown if the string is null. */ // Changes to this method must be copied to the corresponding method in // the XML class to keep full support for Android @@ -2257,10 +920,8 @@ public class JSONObject { /** * Throw an exception if the object is a NaN or infinite number. * - * @param o - * The object to test. - * @throws JSONException - * If o is a non-finite number. + * @param o The object to test. + * @throws JSONException If o is a non-finite number. */ public static void testValidity(Object o) throws JSONException { if (o != null) { @@ -2278,83 +939,6 @@ public class JSONObject { } } - /** - * Produce a JSONArray containing the values of the members of this - * JSONObject. - * - * @param names - * A JSONArray containing a list of key strings. This determines - * the sequence of the values in the result. - * @return A JSONArray of values. - * @throws JSONException - * If any of the values are non-finite numbers. - */ - public JSONArray toJSONArray(JSONArray names) throws JSONException { - if (names == null || names.isEmpty()) { - return null; - } - JSONArray ja = new JSONArray(); - for (int i = 0; i < names.length(); i += 1) { - ja.put(this.opt(names.getString(i))); - } - return ja; - } - - /** - * Make a JSON text of this JSONObject. For compactness, no whitespace is - * added. If this would not result in a syntactically correct JSON text, - * then null will be returned instead. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). - */ - @Override - public String toString() { - try { - return this.toString(0); - } catch (Exception e) { - return null; - } - } - - /** - * Make a pretty-printed JSON text of this JSONObject. - * - *

If indentFactor > 0 and the {@link JSONObject} - * has only one key, then the object will be output on a single line: - *

{@code {"key": 1}}
- * - *

If an object has 2 or more keys, then it will be output across - * multiple lines:

{
-     *  "key1": 1,
-     *  "key2": "value 2",
-     *  "key3": 3
-     * }
- *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @param indentFactor - * The number of spaces to add to each level of indentation. - * @return a printable, displayable, portable, transmittable representation - * of the object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the object contains an invalid number. - */ - public String toString(int indentFactor) throws JSONException { - StringWriter w = new StringWriter(); - synchronized (w.getBuffer()) { - return this.write(w, indentFactor, 0).toString(); - } - } - /** * Make a JSON text of an Object value. If the object has an * value.toJSONString() method, then that method will be used to produce the @@ -2370,20 +954,18 @@ public class JSONObject { *

* Warning: This method assumes that the data structure is acyclical. * - * @param value - * The value to be serialized. + * @param value The value to be serialized. * @return a printable, displayable, transmittable representation of the - * object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the value is or contains an invalid number. + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException If the value is or contains an invalid number. */ public static String valueToString(Object value) throws JSONException { - // moves the implementation to JSONWriter as: - // 1. It makes more sense to be part of the writer class - // 2. For Android support this method is not available. By implementing it in the Writer - // Android users can use the writer with the built in Android JSONObject implementation. + // moves the implementation to JSONWriter as: + // 1. It makes more sense to be part of the writer class + // 2. For Android support this method is not available. By implementing it in the Writer + // Android users can use the writer with the built in Android JSONObject implementation. return JSONWriter.valueToString(value); } @@ -2395,8 +977,7 @@ public class JSONObject { * one of the java packages, turn it into a string. And if it doesn't, try * to wrap it in a JSONObject. If the wrapping fails, then null is returned. * - * @param object - * The object to wrap + * @param object The object to wrap * @return The wrapped value */ public static Object wrap(Object object) { @@ -2440,22 +1021,8 @@ public class JSONObject { } } - /** - * Write the contents of the JSONObject as JSON text to a writer. For - * compactness, no whitespace is added. - *

- * Warning: This method assumes that the data structure is acyclical. - * - * - * @return The writer. - * @throws JSONException - */ - public Writer write(Writer writer) throws JSONException { - return this.write(writer, 0, 0); - } - static final Writer writeValue(Writer writer, Object value, - int indentFactor, int indent) throws JSONException, IOException { + int indentFactor, int indent) throws JSONException, IOException { if (value == null || value.equals(null)) { writer.write("null"); } else if (value instanceof JSONString) { @@ -2469,7 +1036,7 @@ public class JSONObject { } else if (value instanceof Number) { // not all Numbers may match actual JSON Numbers. i.e. fractions or Imaginary final String numberAsString = numberToString((Number) value); - if(NUMBER_PATTERN.matcher(numberAsString).matches()) { + if (NUMBER_PATTERN.matcher(numberAsString).matches()) { writer.write(numberAsString); } else { // The Number value is not a valid JSON number. @@ -2479,7 +1046,7 @@ public class JSONObject { } else if (value instanceof Boolean) { writer.write(value.toString()); } else if (value instanceof Enum) { - writer.write(quote(((Enum)value).name())); + writer.write(quote(((Enum) value).name())); } else if (value instanceof JSONObject) { ((JSONObject) value).write(writer, indentFactor, indent); } else if (value instanceof JSONArray) { @@ -2505,12 +1072,1227 @@ public class JSONObject { } /** - * Write the contents of the JSONObject as JSON text to a writer. - * + * Create a new JSONException in a common format for incorrect conversions. + * + * @param key name of the key + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + String key, + String valueType, + Throwable cause) { + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + "." + , cause); + } + + /** + * Create a new JSONException in a common format for incorrect conversions. + * + * @param key name of the key + * @param valueType the type of value being coerced to + * @param cause optional cause of the coercion failure + * @return JSONException that can be thrown. + */ + private static JSONException wrongValueFormatException( + String key, + String valueType, + Object value, + Throwable cause) { + return new JSONException( + "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value + ")." + , cause); + } + + /** + * Accumulate values under a key. It is similar to the put method except + * that if there is already an object stored under the key then a JSONArray + * is stored under the key to hold all of the accumulated values. If there + * is already a JSONArray, then the new value is appended to it. In + * contrast, the put method replaces the previous value. + *

+ * If only one value is accumulated that is not a JSONArray, then the result + * will be the same as using put. But if multiple values are accumulated, + * then the result will be like append. + * + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject accumulate(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, + value instanceof JSONArray ? new JSONArray().put(value) + : value); + } else if (object instanceof JSONArray) { + ((JSONArray) object).put(value); + } else { + this.put(key, new JSONArray().put(object).put(value)); + } + return this; + } + + /** + * Append values to the array under a key. If the key does not exist in the + * JSONObject, then the key is put in the JSONObject with its value being a + * JSONArray containing the value parameter. If the key was already + * associated with a JSONArray, then the value parameter is appended to it. + * + * @param key A key string. + * @param value An object to be accumulated under the key. + * @return this. + * @throws JSONException If the value is non-finite number or if the current value associated with + * the key is not a JSONArray. + * @throws NullPointerException If the key is null. + */ + public JSONObject append(String key, Object value) throws JSONException { + testValidity(value); + Object object = this.opt(key); + if (object == null) { + this.put(key, new JSONArray().put(value)); + } else if (object instanceof JSONArray) { + this.put(key, ((JSONArray) object).put(value)); + } else { + throw wrongValueFormatException(key, "JSONArray", null, null); + } + return this; + } + + /** + * Get the value object associated with a key. + * + * @param key A key string. + * @return The object associated with the key. + * @throws JSONException if the key is not found. + */ + public Object get(String key) throws JSONException { + if (key == null) { + throw new JSONException("Null key."); + } + Object object = this.opt(key); + if (object == null) { + throw new JSONException("JSONObject[" + quote(key) + "] not found."); + } + return object; + } + + /** + * Get the enum value associated with a key. + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param key A key string. + * @return The enum value associated with the key + * @throws JSONException if the key is not found or if the value cannot be converted + * to an enum. + */ + public > E getEnum(Class clazz, String key) throws JSONException { + E val = optEnum(clazz, key); + if (val == null) { + // JSONException should really take a throwable argument. + // If it did, I would re-implement this with the Enum.valueOf + // method and place any thrown exception in the JSONException + throw wrongValueFormatException(key, "enum of type " + quote(clazz.getSimpleName()), null); + } + return val; + } + + /** + * Get the boolean value associated with a key. + * + * @param key A key string. + * @return The truth. + * @throws JSONException if the value is not a Boolean or the String "true" or + * "false". + */ + public boolean getBoolean(String key) throws JSONException { + Object object = this.get(key); + if (object.equals(Boolean.FALSE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("false"))) { + return false; + } else if (object.equals(Boolean.TRUE) + || (object instanceof String && ((String) object) + .equalsIgnoreCase("true"))) { + return true; + } + throw wrongValueFormatException(key, "Boolean", null); + } + + /** + * Get the BigInteger value associated with a key. + * + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or if the value cannot + * be converted to BigInteger. + */ + public BigInteger getBigInteger(String key) throws JSONException { + Object object = this.get(key); + BigInteger ret = objectToBigInteger(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigInteger", object, null); + } + + /** + * Get the BigDecimal value associated with a key. If the value is float or + * double, the the {@link BigDecimal#BigDecimal(double)} constructor will + * be used. See notes on the constructor for conversion issues that may + * arise. + * + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or if the value + * cannot be converted to BigDecimal. + */ + public BigDecimal getBigDecimal(String key) throws JSONException { + Object object = this.get(key); + BigDecimal ret = objectToBigDecimal(object, null); + if (ret != null) { + return ret; + } + throw wrongValueFormatException(key, "BigDecimal", object, null); + } + + /** + * Get the double value associated with a key. + * + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public double getDouble(String key) throws JSONException { + final Object object = this.get(key); + if (object instanceof Number) { + return ((Number) object).doubleValue(); + } + try { + return Double.parseDouble(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "double", e); + } + } + + /** + * Get the float value associated with a key. + * + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public float getFloat(String key) throws JSONException { + final Object object = this.get(key); + if (object instanceof Number) { + return ((Number) object).floatValue(); + } + try { + return Float.parseFloat(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "float", e); + } + } + + /** + * Get the Number value associated with a key. + * + * @param key A key string. + * @return The numeric value. + * @throws JSONException if the key is not found or if the value is not a Number + * object and cannot be converted to a number. + */ + public Number getNumber(String key) throws JSONException { + Object object = this.get(key); + try { + if (object instanceof Number) { + return (Number) object; + } + return stringToNumber(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "number", e); + } + } + + /** + * Get the int value associated with a key. + * + * @param key A key string. + * @return The integer value. + * @throws JSONException if the key is not found or if the value cannot be converted + * to an integer. + */ + public int getInt(String key) throws JSONException { + final Object object = this.get(key); + if (object instanceof Number) { + return ((Number) object).intValue(); + } + try { + return Integer.parseInt(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "int", e); + } + } + + /** + * Get the JSONArray value associated with a key. + * + * @param key A key string. + * @return A JSONArray which is the value. + * @throws JSONException if the key is not found or if the value is not a JSONArray. + */ + public JSONArray getJSONArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + return (JSONArray) object; + } + throw wrongValueFormatException(key, "JSONArray", null); + } + + /** + * Get the JSONObject value associated with a key. + * + * @param key A key string. + * @return A JSONObject which is the value. + * @throws JSONException if the key is not found or if the value is not a JSONObject. + */ + public JSONObject getJSONObject(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONObject) { + return (JSONObject) object; + } + throw wrongValueFormatException(key, "JSONObject", null); + } + + /** + * Get the long value associated with a key. + * + * @param key A key string. + * @return The long value. + * @throws JSONException if the key is not found or if the value cannot be converted + * to a long. + */ + public long getLong(String key) throws JSONException { + final Object object = this.get(key); + if (object instanceof Number) { + return ((Number) object).longValue(); + } + try { + return Long.parseLong(object.toString()); + } catch (Exception e) { + throw wrongValueFormatException(key, "long", e); + } + } + + public UUID getUUID(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof UUID) { + return (UUID) object; + } else if (object instanceof String) { + return UUID.fromString(String.valueOf(object)); + } + throw wrongValueFormatException(key, "UUID", null); + } + + /** + * Get the string associated with a key. + * + * @param key A key string. + * @return A string which is the value. + * @throws JSONException if there is no string value for the key. + */ + public String getString(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof String) { + return (String) object; + } + throw wrongValueFormatException(key, "string", null); + } + + public byte getByte(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof Byte) { + return (byte) object; + } + throw wrongValueFormatException(key, "byte", null); + } + + public byte[] getByteArray(String key) throws JSONException { + Object object = this.get(key); + if (object instanceof JSONArray) { + JSONArray jsonArray = (JSONArray) object; + byte[] array = new byte[jsonArray.length()]; + for (int k = 0; k < jsonArray.length(); k++) { + array[k] = Byte.valueOf(String.valueOf(jsonArray.get(k))); + } + return array; + } + if (object instanceof byte[]) { + return (byte[]) object; + } + throw wrongValueFormatException(key, "byteArray", null); + } + + /** + * Determine if the JSONObject contains a specific key. + * + * @param key A key string. + * @return true if the key exists in the JSONObject. + */ + public boolean has(String key) { + return this.map.containsKey(key); + } + + /** + * Increment a property of a JSONObject. If there is no such property, + * create one with a value of 1 (Integer). If there is such a property, and if it is + * an Integer, Long, Double, Float, BigInteger, or BigDecimal then add one to it. + * No overflow bounds checking is performed, so callers should initialize the key + * prior to this call with an appropriate type that can handle the maximum expected + * value. + * + * @param key A key string. + * @return this. + * @throws JSONException If there is already a property with this name that is not an + * Integer, Long, Double, or Float. + */ + public JSONObject increment(String key) throws JSONException { + Object value = this.opt(key); + if (value == null) { + this.put(key, 1); + } else if (value instanceof Integer) { + this.put(key, ((Integer) value).intValue() + 1); + } else if (value instanceof Long) { + this.put(key, ((Long) value).longValue() + 1L); + } else if (value instanceof BigInteger) { + this.put(key, ((BigInteger) value).add(BigInteger.ONE)); + } else if (value instanceof Float) { + this.put(key, ((Float) value).floatValue() + 1.0f); + } else if (value instanceof Double) { + this.put(key, ((Double) value).doubleValue() + 1.0d); + } else if (value instanceof BigDecimal) { + this.put(key, ((BigDecimal) value).add(BigDecimal.ONE)); + } else { + throw new JSONException("Unable to increment [" + quote(key) + "]."); + } + return this; + } + + /** + * Determine if the value associated with the key is null or if there is no + * value. + * + * @param key A key string. + * @return true if there is no value associated with the key or if the value + * is the JSONObject.NULL object. + */ + public boolean isNull(String key) { + return JSONObject.NULL.equals(this.opt(key)); + } + + /** + * Get an enumeration of the keys of the JSONObject. Modifying this key Set will also + * modify the JSONObject. Use with caution. + * + * @return An iterator of the keys. + * @see Set#iterator() + */ + public Iterator keys() { + return this.keySet().iterator(); + } + + /** + * Get a set of keys of the JSONObject. Modifying this key Set will also modify the + * JSONObject. Use with caution. + * + * @return A keySet. + * @see Map#keySet() + */ + public Set keySet() { + return this.map.keySet(); + } + + /** + * Get a set of entries of the JSONObject. These are raw values and may not + * match what is returned by the JSONObject get* and opt* functions. Modifying + * the returned EntrySet or the Entry objects contained therein will modify the + * backing JSONObject. This does not return a clone or a read-only view. + *

+ * Use with caution. + * + * @return An Entry Set + * @see Map#entrySet() + */ + protected Set> entrySet() { + return this.map.entrySet(); + } + + /** + * Get the number of keys stored in the JSONObject. + * + * @return The number of keys in the JSONObject. + */ + public int length() { + return this.map.size(); + } + + /** + * Check if JSONObject is empty. + * + * @return true if JSONObject is empty, otherwise false. + */ + public boolean isEmpty() { + return this.map.isEmpty(); + } + + /** + * Produce a JSONArray containing the names of the elements of this + * JSONObject. + * + * @return A JSONArray containing the key strings, or null if the JSONObject + * is empty. + */ + public JSONArray names() { + if (this.map.isEmpty()) { + return null; + } + return new JSONArray(this.map.keySet()); + } + + /** + * Get an optional value associated with a key. + * + * @param key A key string. + * @return An object which is the value, or null if there is no value. + */ + public Object opt(String key) { + return key == null ? null : this.map.get(key); + } + + /** + * Get the enum value associated with a key. + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param key A key string. + * @return The enum value associated with the key or null if not found + */ + public > E optEnum(Class clazz, String key) { + return this.optEnum(clazz, key, null); + } + + /** + * Get the enum value associated with a key. + * + * @param Enum Type + * @param clazz The type of enum to retrieve. + * @param key A key string. + * @param defaultValue The default in case the value is not found + * @return The enum value associated with the key or defaultValue + * if the value is not found or cannot be assigned to clazz + */ + public > E optEnum(Class clazz, String key, E defaultValue) { + try { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (clazz.isAssignableFrom(val.getClass())) { + // we just checked it! + @SuppressWarnings("unchecked") + E myE = (E) val; + return myE; + } + return Enum.valueOf(clazz, val.toString()); + } catch (IllegalArgumentException e) { + return defaultValue; + } catch (NullPointerException e) { + return defaultValue; + } + } + + /** + * Get an optional boolean associated with a key. It returns false if there + * is no such key, or if the value is not Boolean.TRUE or the String "true". + * + * @param key A key string. + * @return The truth. + */ + public boolean optBoolean(String key) { + return this.optBoolean(key, false); + } + + /** + * Get an optional boolean associated with a key. It returns the + * defaultValue if there is no such key, or if it is not a Boolean or the + * String "true" or "false" (case insensitive). + * + * @param key A key string. + * @param defaultValue The default. + * @return The truth. + */ + public boolean optBoolean(String key, boolean defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Boolean) { + return ((Boolean) val).booleanValue(); + } + try { + // we'll use the get anyway because it does string conversion. + return this.getBoolean(key); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional BigDecimal associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. If the value + * is float or double, then the {@link BigDecimal#BigDecimal(double)} + * constructor will be used. See notes on the constructor for conversion + * issues that may arise. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public BigDecimal optBigDecimal(String key, BigDecimal defaultValue) { + Object val = this.opt(key); + return objectToBigDecimal(val, defaultValue); + } + + /** + * Get an optional BigInteger associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public BigInteger optBigInteger(String key, BigInteger defaultValue) { + Object val = this.opt(key); + return objectToBigInteger(val, defaultValue); + } + + /** + * Get an optional double associated with a key, or NaN if there is no such + * key or if its value is not a number. If the value is a string, an attempt + * will be made to evaluate it as a number. + * + * @param key A string which is the key. + * @return An object which is the value. + */ + public double optDouble(String key) { + return this.optDouble(key, Double.NaN); + } + + /** + * Get an optional double associated with a key, or the defaultValue if + * there is no such key or if its value is not a number. If the value is a + * string, an attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public double optDouble(String key, double defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final double doubleValue = val.doubleValue(); + // if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) { + // return defaultValue; + // } + return doubleValue; + } + + /** + * Get the optional double value associated with an index. NaN is returned + * if there is no value for the index, or if the value is not a number and + * cannot be converted to a number. + * + * @param key A key string. + * @return The value. + */ + public float optFloat(String key) { + return this.optFloat(key, Float.NaN); + } + + /** + * Get the optional double value associated with an index. The defaultValue + * is returned if there is no value for the index, or if the value is not a + * number and cannot be converted to a number. + * + * @param key A key string. + * @param defaultValue The default value. + * @return The value. + */ + public float optFloat(String key, float defaultValue) { + Number val = this.optNumber(key); + if (val == null) { + return defaultValue; + } + final float floatValue = val.floatValue(); + // if (Float.isNaN(floatValue) || Float.isInfinite(floatValue)) { + // return defaultValue; + // } + return floatValue; + } + + /** + * Get an optional int value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public int optInt(String key) { + return this.optInt(key, 0); + } + + /** + * Get an optional int value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public int optInt(String key, int defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + return val.intValue(); + } + + /** + * Get an optional JSONArray associated with a key. It returns null if there + * is no such key, or if its value is not a JSONArray. + * + * @param key A key string. + * @return A JSONArray which is the value. + */ + public JSONArray optJSONArray(String key) { + Object o = this.opt(key); + return o instanceof JSONArray ? (JSONArray) o : null; + } + + /** + * Get an optional JSONObject associated with a key. It returns null if + * there is no such key, or if its value is not a JSONObject. + * + * @param key A key string. + * @return A JSONObject which is the value. + */ + public JSONObject optJSONObject(String key) { + Object object = this.opt(key); + return object instanceof JSONObject ? (JSONObject) object : null; + } + + /** + * Get an optional long value associated with a key, or zero if there is no + * such key or if the value is not a number. If the value is a string, an + * attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @return An object which is the value. + */ + public long optLong(String key) { + return this.optLong(key, 0); + } + + /** + * Get an optional long value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public long optLong(String key, long defaultValue) { + final Number val = this.optNumber(key, null); + if (val == null) { + return defaultValue; + } + + return val.longValue(); + } + + /** + * Get an optional {@link Number} value associated with a key, or null + * if there is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number ({@link BigDecimal}). This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key A key string. + * @return An object which is the value. + */ + public Number optNumber(String key) { + return this.optNumber(key, null); + } + + /** + * Get an optional {@link Number} value associated with a key, or the default if there + * is no such key or if the value is not a number. If the value is a string, + * an attempt will be made to evaluate it as a number. This method + * would be used in cases where type coercion of the number value is unwanted. + * + * @param key A key string. + * @param defaultValue The default. + * @return An object which is the value. + */ + public Number optNumber(String key, Number defaultValue) { + Object val = this.opt(key); + if (NULL.equals(val)) { + return defaultValue; + } + if (val instanceof Number) { + return (Number) val; + } + + try { + return stringToNumber(val.toString()); + } catch (Exception e) { + return defaultValue; + } + } + + /** + * Get an optional string associated with a key. It returns an empty string + * if there is no such key. If the value is not a string and is not null, + * then it is converted to a string. + * + * @param key A key string. + * @return A string which is the value. + */ + public String optString(String key) { + return this.optString(key, ""); + } + + /** + * Get an optional string associated with a key. It returns the defaultValue + * if there is no such key. + * + * @param key A key string. + * @param defaultValue The default. + * @return A string which is the value. + */ + public String optString(String key, String defaultValue) { + Object object = this.opt(key); + return NULL.equals(object) ? defaultValue : object.toString(); + } + + /** + * Populates the internal map of the JSONObject with the bean properties. The + * bean can not be recursive. + * + * @param bean the bean + * @see JSONObject#JSONObject(Object) + */ + private void populateMap(Object bean) { + Class klass = bean.getClass(); + + // If klass is a System class then set includeSuperClass to false. + + boolean includeSuperClass = klass.getClassLoader() != null; + + Method[] methods = includeSuperClass ? klass.getMethods() : klass.getDeclaredMethods(); + for (final Method method : methods) { + final int modifiers = method.getModifiers(); + if (Modifier.isPublic(modifiers) + && !Modifier.isStatic(modifiers) + && method.getParameterTypes().length == 0 + && !method.isBridge() + && method.getReturnType() != Void.TYPE + && isValidMethodName(method.getName())) { + final String key = getKeyNameFromMethod(method); + if (key != null && !key.isEmpty()) { + try { + final Object result = method.invoke(bean); + if (result != null) { + this.map.put(key, wrap(result)); + // we don't use the result anywhere outside of wrap + // if it's a resource we should be sure to close it + // after calling toString + if (result instanceof Closeable) { + try { + ((Closeable) result).close(); + } catch (IOException ignore) { + } + } + } + } catch (IllegalAccessException ignore) { + } catch (IllegalArgumentException ignore) { + } catch (InvocationTargetException ignore) { + } + } + } + } + } + + /** + * Put a key/boolean pair in the JSONObject. + * + * @param key A key string. + * @param value A boolean which is the value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, boolean value) throws JSONException { + return this.put(key, value ? Boolean.TRUE : Boolean.FALSE); + } + + public JSONObject put(String key, byte value) throws JSONException { + return this.put(key, Byte.valueOf(value)); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONArray which is produced from a Collection. + * + * @param key A key string. + * @param value A Collection value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, Collection value) throws JSONException { + return this.put(key, new JSONArray(value)); + } + + /** + * Put a key/double pair in the JSONObject. + * + * @param key A key string. + * @param value A double which is the value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, double value) throws JSONException { + return this.put(key, Double.valueOf(value)); + } + + /** + * Put a key/float pair in the JSONObject. + * + * @param key A key string. + * @param value A float which is the value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, float value) throws JSONException { + return this.put(key, Float.valueOf(value)); + } + + /** + * Put a key/int pair in the JSONObject. + * + * @param key A key string. + * @param value An int which is the value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, int value) throws JSONException { + return this.put(key, Integer.valueOf(value)); + } + + /** + * Put a key/long pair in the JSONObject. + * + * @param key A key string. + * @param value A long which is the value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, long value) throws JSONException { + return this.put(key, Long.valueOf(value)); + } + + /** + * Put a key/value pair in the JSONObject, where the value will be a + * JSONObject which is produced from a Map. + * + * @param key A key string. + * @param value A Map value. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, Map value) throws JSONException { + return this.put(key, new JSONObject(value)); + } + + /** + * Put a key/value pair in the JSONObject. If the value is null, then the + * key will be removed from the JSONObject if it is present. + * + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is non-finite number. + * @throws NullPointerException If the key is null. + */ + public JSONObject put(String key, Object value) throws JSONException { + if (key == null) { + throw new NullPointerException("Null key."); + } + if (value != null) { + testValidity(value); + this.map.put(key, value); + } else { + this.remove(key); + } + return this; + } + + public JSONObject put(String key, byte[] bytes) throws JSONException { + return this.put(key, (Object) bytes); + } + + public JSONObject put(String key, UUID uuid) throws JSONException { + return this.put(key, uuid.toString()); + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null, and only if there is not already a member with that + * name. + * + * @param key key to insert into + * @param value value to insert + * @return this. + * @throws JSONException if the key is a duplicate + */ + public JSONObject putOnce(String key, Object value) throws JSONException { + if (key != null && value != null) { + if (this.opt(key) != null) { + throw new JSONException("Duplicate key \"" + key + "\""); + } + return this.put(key, value); + } + return this; + } + + /** + * Put a key/value pair in the JSONObject, but only if the key and the value + * are both non-null. + * + * @param key A key string. + * @param value An object which is the value. It should be of one of these + * types: Boolean, Double, Integer, JSONArray, JSONObject, Long, + * String, or the JSONObject.NULL object. + * @return this. + * @throws JSONException If the value is a non-finite number. + */ + public JSONObject putOpt(String key, Object value) throws JSONException { + if (key != null && value != null) { + return this.put(key, value); + } + return this; + } + + /** + * Creates a JSONPointer using an initialization string and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *

+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer string: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(String jsonPointer) { + return query(new JSONPointer(jsonPointer)); + } + + /** + * Uses a user initialized JSONPointer and tries to + * match it to an item within this JSONObject. For example, given a + * JSONObject initialized with this document: + *
+     * {
+     *     "a":{"b":"c"}
+     * }
+     * 
+ * and this JSONPointer: + *
+     * "/a/b"
+     * 
+ * Then this method will return the String "c". + * A JSONPointerException may be thrown from code called by this method. + * + * @param jsonPointer string that can be used to create a JSONPointer + * @return the item matched by the JSONPointer, otherwise null + */ + public Object query(JSONPointer jsonPointer) { + return jsonPointer.queryFrom(this); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer the string representation of the JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(String jsonPointer) { + return optQuery(new JSONPointer(jsonPointer)); + } + + /** + * Queries and returns a value from this object using {@code jsonPointer}, or + * returns null if the query fails due to a missing key. + * + * @param jsonPointer The JSON pointer + * @return the queried value or {@code null} + * @throws IllegalArgumentException if {@code jsonPointer} has invalid syntax + */ + public Object optQuery(JSONPointer jsonPointer) { + try { + return jsonPointer.queryFrom(this); + } catch (JSONPointerException e) { + return null; + } + } + + /** + * Remove a name and its value, if present. + * + * @param key The name to be removed. + * @return The value that was associated with the name, or null if there was + * no value. + */ + public Object remove(String key) { + return this.map.remove(key); + } + + /** + * Determine if two JSONObjects are similar. + * They must contain the same set of names which must be associated with + * similar values. + * + * @param other The other JSONObject + * @return true if they are equal + */ + public boolean similar(Object other) { + try { + if (!(other instanceof JSONObject)) { + return false; + } + if (!this.keySet().equals(((JSONObject) other).keySet())) { + return false; + } + for (final Entry entry : this.entrySet()) { + String name = entry.getKey(); + Object valueThis = entry.getValue(); + Object valueOther = ((JSONObject) other).get(name); + if (valueThis == valueOther) { + continue; + } + if (valueThis == null) { + return false; + } + if (valueThis instanceof JSONObject) { + if (!((JSONObject) valueThis).similar(valueOther)) { + return false; + } + } else if (valueThis instanceof JSONArray) { + if (!((JSONArray) valueThis).similar(valueOther)) { + return false; + } + } else if (!valueThis.equals(valueOther)) { + return false; + } + } + return true; + } catch (Throwable exception) { + return false; + } + } + + /** + * Produce a JSONArray containing the values of the members of this + * JSONObject. + * + * @param names A JSONArray containing a list of key strings. This determines + * the sequence of the values in the result. + * @return A JSONArray of values. + * @throws JSONException If any of the values are non-finite numbers. + */ + public JSONArray toJSONArray(JSONArray names) throws JSONException { + if (names == null || names.isEmpty()) { + return null; + } + JSONArray ja = new JSONArray(); + for (int i = 0; i < names.length(); i += 1) { + ja.put(this.opt(names.getString(i))); + } + return ja; + } + + /** + * Make a JSON text of this JSONObject. For compactness, no whitespace is + * added. If this would not result in a syntactically correct JSON text, + * then null will be returned instead. + *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return a printable, displayable, portable, transmittable representation + * of the object, beginning with { (left + * brace) and ending with } (right + * brace). + */ + @Override + public String toString() { + try { + return this.toString(0); + } catch (Exception e) { + return null; + } + } + + /** + * Make a pretty-printed JSON text of this JSONObject. + * *

If indentFactor > 0 and the {@link JSONObject} * has only one key, then the object will be output on a single line: *

{@code {"key": 1}}
- * + * *

If an object has 2 or more keys, then it will be output across * multiple lines:

{
      *  "key1": 1,
@@ -2521,12 +2303,54 @@ public class JSONObject {
      * Warning: This method assumes that the data structure is acyclical.
      * 
      *
-     * @param writer
-     *            Writes the serialized JSON
-     * @param indentFactor
-     *            The number of spaces to add to each level of indentation.
-     * @param indent
-     *            The indentation of the top level.
+     * @param indentFactor The number of spaces to add to each level of indentation.
+     * @return a printable, displayable, portable, transmittable representation
+     * of the object, beginning with { (left
+     * brace) and ending with } (right
+     * brace).
+     * @throws JSONException If the object contains an invalid number.
+     */
+    public String toString(int indentFactor) throws JSONException {
+        StringWriter w = new StringWriter();
+        synchronized (w.getBuffer()) {
+            return this.write(w, indentFactor, 0).toString();
+        }
+    }
+
+    /**
+     * Write the contents of the JSONObject as JSON text to a writer. For
+     * compactness, no whitespace is added.
+     * 

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @return The writer. + * @throws JSONException + */ + public Writer write(Writer writer) throws JSONException { + return this.write(writer, 0, 0); + } + + /** + * Write the contents of the JSONObject as JSON text to a writer. + * + *

If indentFactor > 0 and the {@link JSONObject} + * has only one key, then the object will be output on a single line: + *

{@code {"key": 1}}
+ * + *

If an object has 2 or more keys, then it will be output across + * multiple lines:

{
+     *  "key1": 1,
+     *  "key2": "value 2",
+     *  "key3": 3
+     * }
+ *

+ * Warning: This method assumes that the data structure is acyclical. + * + * + * @param writer Writes the serialized JSON + * @param indentFactor The number of spaces to add to each level of indentation. + * @param indent The indentation of the top level. * @return The writer. * @throws JSONException */ @@ -2538,21 +2362,21 @@ public class JSONObject { writer.write('{'); if (length == 1) { - final Entry entry = this.entrySet().iterator().next(); + final Entry entry = this.entrySet().iterator().next(); final String key = entry.getKey(); writer.write(quote(key)); writer.write(':'); if (indentFactor > 0) { writer.write(' '); } - try{ + try { writeValue(writer, entry.getValue(), indentFactor, indent); } catch (Exception e) { throw new JSONException("Unable to write JSONObject value for key: " + key, e); } } else if (length != 0) { final int newIndent = indent + indentFactor; - for (final Entry entry : this.entrySet()) { + for (final Entry entry : this.entrySet()) { if (needsComma) { writer.write(','); } @@ -2611,37 +2435,55 @@ public class JSONObject { } return results; } - + /** - * Create a new JSONException in a common format for incorrect conversions. - * @param key name of the key - * @param valueType the type of value being coerced to - * @param cause optional cause of the coercion failure - * @return JSONException that can be thrown. + * JSONObject.NULL is equivalent to the value that JavaScript calls null, + * whilst Java's null is equivalent to the value that JavaScript calls + * undefined. */ - private static JSONException wrongValueFormatException( - String key, - String valueType, - Throwable cause) { - return new JSONException( - "JSONObject[" + quote(key) + "] is not a " + valueType + "." - , cause); - } - - /** - * Create a new JSONException in a common format for incorrect conversions. - * @param key name of the key - * @param valueType the type of value being coerced to - * @param cause optional cause of the coercion failure - * @return JSONException that can be thrown. - */ - private static JSONException wrongValueFormatException( - String key, - String valueType, - Object value, - Throwable cause) { - return new JSONException( - "JSONObject[" + quote(key) + "] is not a " + valueType + " (" + value + ")." - , cause); + private static final class Null { + + /** + * There is only intended to be a single instance of the NULL object, + * so the clone method returns itself. + * + * @return NULL. + */ + @Override + protected final Object clone() { + return this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @param object An object to test for nullness. + * @return true if the object parameter is the JSONObject.NULL object or + * null. + */ + @Override + public boolean equals(Object object) { + return object == null || object == this; + } + + /** + * A Null object is equal to the null value and to itself. + * + * @return always returns 0. + */ + @Override + public int hashCode() { + return 0; + } + + /** + * Get the "null" string value. + * + * @return The string "null". + */ + @Override + public String toString() { + return "null"; + } } } diff --git a/src/main/java/json/JSONPointer.java b/src/main/java/json/JSONPointer.java index bef1ee0..28a8a31 100644 --- a/src/main/java/json/JSONPointer.java +++ b/src/main/java/json/JSONPointer.java @@ -36,18 +36,18 @@ SOFTWARE. /** * A JSON Pointer is a simple query language defined for JSON documents by * RFC 6901. - * + *

* In a nutshell, JSONPointer allows the user to navigate into a JSON document * using strings, and retrieve targeted objects, like a simple form of XPATH. * Path segments are separated by the '/' char, which signifies the root of - * the document when it appears as the first char of the string. Array + * the document when it appears as the first char of the string. Array * elements are navigated using ordinals, counting from 0. JSONPointer strings * may be extended to any arbitrary number of segments. If the navigation * is successful, the matched item is returned. A matched item may be a - * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building + * JSONObject, a JSONArray, or a JSON value. If the JSONPointer string building * fails, an appropriate exception is thrown. If the navigation fails to find - * a match, a JSONPointerException is thrown. - * + * a match, a JSONPointerException is thrown. + * * @author JSON.org * @version 2016-05-14 */ @@ -55,76 +55,6 @@ public class JSONPointer { // used for URL encoding and decoding private static final String ENCODING = "utf-8"; - - /** - * This class allows the user to build a JSONPointer in steps, using - * exactly one segment in each step. - */ - public static class Builder { - - // Segments for the eventual JSONPointer string - private final List refTokens = new ArrayList(); - - /** - * Creates a {@code JSONPointer} instance using the tokens previously set using the - * {@link #append(String)} method calls. - */ - public JSONPointer build() { - return new JSONPointer(this.refTokens); - } - - /** - * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. - * - * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the - * argument of this method MUST NOT be escaped. If you want to query the property called - * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no - * need to escape it as {@code "a~0b"}. - * - * @param token the new token to be appended to the list - * @return {@code this} - * @throws NullPointerException if {@code token} is null - */ - public Builder append(String token) { - if (token == null) { - throw new NullPointerException("token cannot be null"); - } - this.refTokens.add(token); - return this; - } - - /** - * Adds an integer to the reference token list. Although not necessarily, mostly this token will - * denote an array index. - * - * @param arrayIndex the array index to be added to the token list - * @return {@code this} - */ - public Builder append(int arrayIndex) { - this.refTokens.add(String.valueOf(arrayIndex)); - return this; - } - } - - /** - * Static factory method for {@link Builder}. Example usage: - * - *


-     * JSONPointer pointer = JSONPointer.builder()
-     *       .append("obj")
-     *       .append("other~key").append("another/key")
-     *       .append("\"")
-     *       .append(0)
-     *       .build();
-     * 
- * - * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained - * {@link Builder#append(String)} calls. - */ - public static Builder builder() { - return new Builder(); - } - // Segments for the JSONPointer string private final List refTokens; @@ -132,7 +62,7 @@ public class JSONPointer { * Pre-parses and initializes a new {@code JSONPointer} instance. If you want to * evaluate the same JSON Pointer on different JSON documents then it is recommended * to keep the {@code JSONPointer} instances due to performance considerations. - * + * * @param pointer the JSON String or URI Fragment representation of the JSON pointer. * @throws IllegalArgumentException if {@code pointer} is not a valid JSON pointer */ @@ -163,7 +93,7 @@ public class JSONPointer { do { prevSlashIdx = slashIdx + 1; slashIdx = refs.indexOf('/', prevSlashIdx); - if(prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { + if (prevSlashIdx == slashIdx || prevSlashIdx == refs.length()) { // found 2 slashes in a row ( obj//next ) // or single slash at the end of a string ( obj/test/ ) this.refTokens.add(""); @@ -186,18 +116,79 @@ public class JSONPointer { this.refTokens = new ArrayList(refTokens); } + /** + * Static factory method for {@link Builder}. Example usage: + * + *

+     * JSONPointer pointer = JSONPointer.builder()
+     *       .append("obj")
+     *       .append("other~key").append("another/key")
+     *       .append("\"")
+     *       .append(0)
+     *       .build();
+     * 
+ * + * @return a builder instance which can be used to construct a {@code JSONPointer} instance by chained + * {@link Builder#append(String)} calls. + */ + public static Builder builder() { + return new Builder(); + } + private static String unescape(String token) { return token.replace("~1", "/").replace("~0", "~") .replace("\\\"", "\"") .replace("\\\\", "\\"); } + /** + * Matches a JSONArray element by ordinal position + * + * @param current the JSONArray to be evaluated + * @param indexToken the array index in string form + * @return the matched object. If no matching item is found a + * @throws JSONPointerException is thrown if the index is out of bounds + */ + private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { + try { + int index = Integer.parseInt(indexToken); + JSONArray currentArr = (JSONArray) current; + if (index >= currentArr.length()) { + throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, + Integer.valueOf(currentArr.length()))); + } + try { + return currentArr.get(index); + } catch (JSONException e) { + throw new JSONPointerException("Error reading value at index position " + index, e); + } + } catch (NumberFormatException e) { + throw new JSONPointerException(format("%s is not an array index", indexToken), e); + } + } + + /** + * Escapes path segment values to an unambiguous form. + * The escape char to be inserted is '~'. The chars to be escaped + * are ~, which maps to ~0, and /, which maps to ~1. Backslashes + * and double quote chars are also escaped. + * + * @param token the JSONPointer segment value to be escaped + * @return the escaped value for the token + */ + private static String escape(String token) { + return token.replace("~", "~0") + .replace("/", "~1") + .replace("\\", "\\\\") + .replace("\"", "\\\""); + } + /** * Evaluates this JSON Pointer on the given {@code document}. The {@code document} * is usually a {@link JSONObject} or a {@link JSONArray} instance, but the empty * JSON Pointer ({@code ""}) can be evaluated on any JSON values and in such case the - * returned value will be {@code document} itself. - * + * returned value will be {@code document} itself. + * * @param document the JSON document which should be the subject of querying. * @return the result of the evaluation * @throws JSONPointerException if an error occurs during evaluation @@ -221,31 +212,6 @@ public class JSONPointer { return current; } - /** - * Matches a JSONArray element by ordinal position - * @param current the JSONArray to be evaluated - * @param indexToken the array index in string form - * @return the matched object. If no matching item is found a - * @throws JSONPointerException is thrown if the index is out of bounds - */ - private static Object readByIndexToken(Object current, String indexToken) throws JSONPointerException { - try { - int index = Integer.parseInt(indexToken); - JSONArray currentArr = (JSONArray) current; - if (index >= currentArr.length()) { - throw new JSONPointerException(format("index %s is out of bounds - the array has %d elements", indexToken, - Integer.valueOf(currentArr.length()))); - } - try { - return currentArr.get(index); - } catch (JSONException e) { - throw new JSONPointerException("Error reading value at index position " + index, e); - } - } catch (NumberFormatException e) { - throw new JSONPointerException(format("%s is not an array index", indexToken), e); - } - } - /** * Returns a string representing the JSONPointer path value using string * representation @@ -253,27 +219,12 @@ public class JSONPointer { @Override public String toString() { StringBuilder rval = new StringBuilder(""); - for (String token: this.refTokens) { + for (String token : this.refTokens) { rval.append('/').append(escape(token)); } return rval.toString(); } - /** - * Escapes path segment values to an unambiguous form. - * The escape char to be inserted is '~'. The chars to be escaped - * are ~, which maps to ~0, and /, which maps to ~1. Backslashes - * and double quote chars are also escaped. - * @param token the JSONPointer segment value to be escaped - * @return the escaped value for the token - */ - private static String escape(String token) { - return token.replace("~", "~0") - .replace("/", "~1") - .replace("\\", "\\\\") - .replace("\"", "\\\""); - } - /** * Returns a string representing the JSONPointer path value using URI * fragment identifier representation @@ -289,5 +240,55 @@ public class JSONPointer { throw new RuntimeException(e); } } - + + /** + * This class allows the user to build a JSONPointer in steps, using + * exactly one segment in each step. + */ + public static class Builder { + + // Segments for the eventual JSONPointer string + private final List refTokens = new ArrayList(); + + /** + * Creates a {@code JSONPointer} instance using the tokens previously set using the + * {@link #append(String)} method calls. + */ + public JSONPointer build() { + return new JSONPointer(this.refTokens); + } + + /** + * Adds an arbitrary token to the list of reference tokens. It can be any non-null value. + *

+ * Unlike in the case of JSON string or URI fragment representation of JSON pointers, the + * argument of this method MUST NOT be escaped. If you want to query the property called + * {@code "a~b"} then you should simply pass the {@code "a~b"} string as-is, there is no + * need to escape it as {@code "a~0b"}. + * + * @param token the new token to be appended to the list + * @return {@code this} + * @throws NullPointerException if {@code token} is null + */ + public Builder append(String token) { + if (token == null) { + throw new NullPointerException("token cannot be null"); + } + this.refTokens.add(token); + return this; + } + + /** + * Adds an integer to the reference token list. Although not necessarily, mostly this token will + * denote an array index. + * + * @param arrayIndex the array index to be added to the token list + * @return {@code this} + */ + public Builder append(int arrayIndex) { + this.refTokens.add(String.valueOf(arrayIndex)); + return this; + } + } + } diff --git a/src/main/java/json/JSONPointerException.java b/src/main/java/json/JSONPointerException.java index 294d4e3..68eafee 100644 --- a/src/main/java/json/JSONPointerException.java +++ b/src/main/java/json/JSONPointerException.java @@ -27,7 +27,7 @@ SOFTWARE. /** * The JSONPointerException is thrown by {@link JSONPointer} if an error occurs * during evaluating a pointer. - * + * * @author JSON.org * @version 2016-05-13 */ diff --git a/src/main/java/json/JSONPropertyIgnore.java b/src/main/java/json/JSONPropertyIgnore.java index c2b18bd..dd81c36 100644 --- a/src/main/java/json/JSONPropertyIgnore.java +++ b/src/main/java/json/JSONPropertyIgnore.java @@ -40,4 +40,5 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME; * present at any level in the class hierarchy, then the method will * not be serialized from the bean into the JSONObject. */ -public @interface JSONPropertyIgnore { } +public @interface JSONPropertyIgnore { +} diff --git a/src/main/java/json/JSONString.java b/src/main/java/json/JSONString.java index dbf7fb2..0a75428 100644 --- a/src/main/java/json/JSONString.java +++ b/src/main/java/json/JSONString.java @@ -1,4 +1,5 @@ package json; + /** * The JSONString interface allows a toJSONString() * method so that a class can change the behavior of diff --git a/src/main/java/json/JSONStringer.java b/src/main/java/json/JSONStringer.java index 46979bb..5b7b0be 100644 --- a/src/main/java/json/JSONStringer.java +++ b/src/main/java/json/JSONStringer.java @@ -53,6 +53,7 @@ import java.io.StringWriter; * you. Objects and arrays can be nested up to 20 levels deep. *

* This can sometimes be easier than using a JSONObject to build a string. + * * @author JSON.org * @version 2015-12-09 */ @@ -70,6 +71,7 @@ public class JSONStringer extends JSONWriter { * problem in the construction of the JSON text (such as the calls to * array were not properly balanced with calls to * endArray). + * * @return The JSON text. */ @Override diff --git a/src/main/java/json/JSONTokener.java b/src/main/java/json/JSONTokener.java index f7f450e..632d1cf 100644 --- a/src/main/java/json/JSONTokener.java +++ b/src/main/java/json/JSONTokener.java @@ -30,37 +30,54 @@ SOFTWARE. * A JSONTokener takes a source string and extracts characters and tokens from * it. It is used by the JSONObject and JSONArray constructors to parse * JSON source strings. + * * @author JSON.org * @version 2014-05-03 */ public class JSONTokener { - /** current read character position on the current line. */ - private long character; - /** flag to indicate if the end of the input has been found. */ - private boolean eof; - /** current read index of the input. */ - private long index; - /** current line of the input. */ - private long line; - /** previous character read from the input. */ - private char previous; - /** Reader for the input. */ + /** + * Reader for the input. + */ private final Reader reader; - /** flag to indicate that a previous character was requested. */ + /** + * current read character position on the current line. + */ + private long character; + /** + * flag to indicate if the end of the input has been found. + */ + private boolean eof; + /** + * current read index of the input. + */ + private long index; + /** + * current line of the input. + */ + private long line; + /** + * previous character read from the input. + */ + private char previous; + /** + * flag to indicate that a previous character was requested. + */ private boolean usePrevious; - /** the number of characters read in the previous line. */ + /** + * the number of characters read in the previous line. + */ private long characterPreviousLine; /** * Construct a JSONTokener from a Reader. The caller must close the Reader. * - * @param reader A reader. + * @param reader A reader. */ public JSONTokener(Reader reader) { this.reader = reader.markSupported() ? reader - : new BufferedReader(reader); + : new BufferedReader(reader); this.eof = false; this.usePrevious = false; this.previous = 0; @@ -73,6 +90,7 @@ public class JSONTokener { /** * Construct a JSONTokener from an InputStream. The caller must close the input stream. + * * @param inputStream The source. */ public JSONTokener(InputStream inputStream) { @@ -83,47 +101,18 @@ public class JSONTokener { /** * Construct a JSONTokener from a string. * - * @param s A source string. + * @param s A source string. */ public JSONTokener(String s) { this(new StringReader(s)); } - - /** - * Back up one character. This provides a sort of lookahead capability, - * so that you can test for a digit or letter before attempting to parse - * the next number or identifier. - * @throws JSONException Thrown if trying to step back more than 1 step - * or if already at the start of the string - */ - public void back() throws JSONException { - if (this.usePrevious || this.index <= 0) { - throw new JSONException("Stepping back two steps is not supported"); - } - this.decrementIndexes(); - this.usePrevious = true; - this.eof = false; - } - - /** - * Decrements the indexes for the {@link #back()} method based on the previous character read. - */ - private void decrementIndexes() { - this.index--; - if(this.previous=='\r' || this.previous == '\n') { - this.line--; - this.character=this.characterPreviousLine ; - } else if(this.character > 0){ - this.character--; - } - } - /** * Get the hex value of a character (base16). + * * @param c A character between '0' and '9' or between 'A' and 'F' or - * between 'a' and 'f'. - * @return An int between 0 and 15, or -1 if c was not a hex digit. + * between 'a' and 'f'. + * @return An int between 0 and 15, or -1 if c was not a hex digit. */ public static int dehexchar(char c) { if (c >= '0' && c <= '9') { @@ -138,9 +127,39 @@ public class JSONTokener { return -1; } + /** + * Back up one character. This provides a sort of lookahead capability, + * so that you can test for a digit or letter before attempting to parse + * the next number or identifier. + * + * @throws JSONException Thrown if trying to step back more than 1 step + * or if already at the start of the string + */ + public void back() throws JSONException { + if (this.usePrevious || this.index <= 0) { + throw new JSONException("Stepping back two steps is not supported"); + } + this.decrementIndexes(); + this.usePrevious = true; + this.eof = false; + } + + /** + * Decrements the indexes for the {@link #back()} method based on the previous character read. + */ + private void decrementIndexes() { + this.index--; + if (this.previous == '\r' || this.previous == '\n') { + this.line--; + this.character = this.characterPreviousLine; + } else if (this.character > 0) { + this.character--; + } + } + /** * Checks if the end of the input has been reached. - * + * * @return true if at the end of the file and we didn't step back */ public boolean end() { @@ -151,12 +170,13 @@ public class JSONTokener { /** * Determine if the source string still contains characters that next() * can consume. + * * @return true if not yet at the end of the source. * @throws JSONException thrown if there is an error stepping forward - * or backward while checking for more data. + * or backward while checking for more data. */ public boolean more() throws JSONException { - if(this.usePrevious) { + if (this.usePrevious) { return true; } try { @@ -166,7 +186,7 @@ public class JSONTokener { } try { // -1 is EOF, but next() can not consume the null character '\0' - if(this.reader.read() <= 0) { + if (this.reader.read() <= 0) { this.eof = true; return false; } @@ -208,21 +228,22 @@ public class JSONTokener { /** * Increments the internal indexes according to the previous character * read and the character passed as the current character. + * * @param c the current character read. */ private void incrementIndexes(int c) { - if(c > 0) { + if (c > 0) { this.index++; - if(c=='\r') { + if (c == '\r') { this.line++; this.characterPreviousLine = this.character; - this.character=0; - }else if (c=='\n') { - if(this.previous != '\r') { + this.character = 0; + } else if (c == '\n') { + if (this.previous != '\r') { this.line++; this.characterPreviousLine = this.character; } - this.character=0; + this.character = 0; } else { this.character++; } @@ -232,6 +253,7 @@ public class JSONTokener { /** * Consume the next character, and check that it matches a specified * character. + * * @param c The character to match. * @return The character. * @throws JSONException if the character does not match. @@ -239,7 +261,7 @@ public class JSONTokener { public char next(char c) throws JSONException { char n = this.next(); if (n != c) { - if(n > 0) { + if (n > 0) { throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'"); } @@ -252,11 +274,10 @@ public class JSONTokener { /** * Get the next n characters. * - * @param n The number of characters to take. - * @return A string of n characters. - * @throws JSONException - * Substring bounds error if there are not - * n characters remaining in the source string. + * @param n The number of characters to take. + * @return A string of n characters. + * @throws JSONException Substring bounds error if there are not + * n characters remaining in the source string. */ public String next(int n) throws JSONException { if (n == 0) { @@ -279,11 +300,12 @@ public class JSONTokener { /** * Get the next char in the string, skipping whitespace. + * + * @return A character, or 0 if there are no more characters. * @throws JSONException Thrown if there is an error reading the source string. - * @return A character, or 0 if there are no more characters. */ public char nextClean() throws JSONException { - for (;;) { + for (; ; ) { char c = this.next(); if (c == 0 || c > ' ') { return c; @@ -297,62 +319,63 @@ public class JSONTokener { * Backslash processing is done. The formal JSON format does not * allow strings in single quotes, but an implementation is allowed to * accept them. + * * @param quote The quoting character, either - * " (double quote) or - * ' (single quote). - * @return A String. + * " (double quote) or + * ' (single quote). + * @return A String. * @throws JSONException Unterminated string. */ public String nextString(char quote) throws JSONException { char c; StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { c = this.next(); switch (c) { - case 0: - case '\n': - case '\r': - throw this.syntaxError("Unterminated string"); - case '\\': - c = this.next(); - switch (c) { - case 'b': - sb.append('\b'); - break; - case 't': - sb.append('\t'); - break; - case 'n': - sb.append('\n'); - break; - case 'f': - sb.append('\f'); - break; - case 'r': - sb.append('\r'); - break; - case 'u': - try { - sb.append((char)Integer.parseInt(this.next(4), 16)); - } catch (NumberFormatException e) { - throw this.syntaxError("Illegal escape.", e); + case 0: + case '\n': + case '\r': + throw this.syntaxError("Unterminated string"); + case '\\': + c = this.next(); + switch (c) { + case 'b': + sb.append('\b'); + break; + case 't': + sb.append('\t'); + break; + case 'n': + sb.append('\n'); + break; + case 'f': + sb.append('\f'); + break; + case 'r': + sb.append('\r'); + break; + case 'u': + try { + sb.append((char) Integer.parseInt(this.next(4), 16)); + } catch (NumberFormatException e) { + throw this.syntaxError("Illegal escape.", e); + } + break; + case '"': + case '\'': + case '\\': + case '/': + sb.append(c); + break; + default: + throw this.syntaxError("Illegal escape."); } break; - case '"': - case '\'': - case '\\': - case '/': - sb.append(c); - break; default: - throw this.syntaxError("Illegal escape."); - } - break; - default: - if (c == quote) { - return sb.toString(); - } - sb.append(c); + if (c == quote) { + return sb.toString(); + } + sb.append(c); } } } @@ -361,14 +384,15 @@ public class JSONTokener { /** * Get the text up but not including the specified character or the * end of line, whichever comes first. - * @param delimiter A delimiter character. - * @return A string. + * + * @param delimiter A delimiter character. + * @return A string. * @throws JSONException Thrown if there is an error while searching - * for the delimiter + * for the delimiter */ public String nextTo(char delimiter) throws JSONException { StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { char c = this.next(); if (c == delimiter || c == 0 || c == '\n' || c == '\r') { if (c != 0) { @@ -384,15 +408,16 @@ public class JSONTokener { /** * Get the text up but not including one of the specified delimiter * characters or the end of line, whichever comes first. + * * @param delimiters A set of delimiter characters. * @return A string, trimmed. * @throws JSONException Thrown if there is an error while searching - * for the delimiter + * for the delimiter */ public String nextTo(String delimiters) throws JSONException { char c; StringBuilder sb = new StringBuilder(); - for (;;) { + for (; ; ) { c = this.next(); if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') { @@ -409,24 +434,24 @@ public class JSONTokener { /** * Get the next value. The value can be a Boolean, Double, Integer, * JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object. - * @throws JSONException If syntax error. * * @return An object. + * @throws JSONException If syntax error. */ public Object nextValue() throws JSONException { char c = this.nextClean(); String string; switch (c) { - case '"': - case '\'': - return this.nextString(c); - case '{': - this.back(); - return new JSONObject(this); - case '[': - this.back(); - return new JSONArray(this); + case '"': + case '\'': + return this.nextString(c); + case '{': + this.back(); + return new JSONObject(this); + case '[': + this.back(); + return new JSONArray(this); } /* @@ -458,11 +483,12 @@ public class JSONTokener { /** * Skip characters until the next character is the requested character. * If the requested character is not found, no characters are skipped. + * * @param to A character to skip to. * @return The requested character, or zero if the requested character * is not found. * @throws JSONException Thrown if there is an error while searching - * for the to character + * for the to character */ public char skipTo(char to) throws JSONException { char c; @@ -496,7 +522,7 @@ public class JSONTokener { * Make a JSONException to signal a syntax error. * * @param message The error message. - * @return A JSONException object, suitable for throwing + * @return A JSONException object, suitable for throwing */ public JSONException syntaxError(String message) { return new JSONException(message + this.toString()); @@ -505,9 +531,9 @@ public class JSONTokener { /** * Make a JSONException to signal a syntax error. * - * @param message The error message. + * @param message The error message. * @param causedBy The throwable that caused the error. - * @return A JSONException object, suitable for throwing + * @return A JSONException object, suitable for throwing */ public JSONException syntaxError(String message, Throwable causedBy) { return new JSONException(message + this.toString(), causedBy); diff --git a/src/main/java/json/JSONWriter.java b/src/main/java/json/JSONWriter.java index 1576383..044b4ed 100644 --- a/src/main/java/json/JSONWriter.java +++ b/src/main/java/json/JSONWriter.java @@ -54,18 +54,16 @@ SOFTWARE. * you. Objects and arrays can be nested up to 200 levels deep. *

* This can sometimes be easier than using a JSONObject to build a string. + * * @author JSON.org * @version 2016-08-08 */ public class JSONWriter { private static final int maxdepth = 200; - /** - * The comma flag determines if a comma should be output before the next - * value. + * The object/array stack. */ - private boolean comma; - + private final JSONObject stack[]; /** * The current mode. Values: * 'a' (array), @@ -75,21 +73,19 @@ public class JSONWriter { * 'o' (object). */ protected char mode; - - /** - * The object/array stack. - */ - private final JSONObject stack[]; - - /** - * The stack top index. A value of 0 indicates that the stack is empty. - */ - private int top; - /** * The writer that will receive the output. */ protected Appendable writer; + /** + * The comma flag determines if a comma should be output before the next + * value. + */ + private boolean comma; + /** + * The stack top index. A value of 0 indicates that the stack is empty. + */ + private int top; /** * Make a fresh JSONWriter. It can be used to build one JSON text. @@ -102,200 +98,6 @@ public class JSONWriter { this.writer = w; } - /** - * Append a value. - * @param string A string value. - * @return this - * @throws JSONException If the value is out of sequence. - */ - private JSONWriter append(String string) throws JSONException { - if (string == null) { - throw new JSONException("Null pointer"); - } - if (this.mode == 'o' || this.mode == 'a') { - try { - if (this.comma && this.mode == 'a') { - this.writer.append(','); - } - this.writer.append(string); - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - if (this.mode == 'o') { - this.mode = 'k'; - } - this.comma = true; - return this; - } - throw new JSONException("Value out of sequence."); - } - - /** - * Begin appending a new array. All values until the balancing - * endArray will be appended to this array. The - * endArray method must be called to mark the array's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter array() throws JSONException { - if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { - this.push(null); - this.append("["); - this.comma = false; - return this; - } - throw new JSONException("Misplaced array."); - } - - /** - * End something. - * @param m Mode - * @param c Closing character - * @return this - * @throws JSONException If unbalanced. - */ - private JSONWriter end(char m, char c) throws JSONException { - if (this.mode != m) { - throw new JSONException(m == 'a' - ? "Misplaced endArray." - : "Misplaced endObject."); - } - this.pop(m); - try { - this.writer.append(c); - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - this.comma = true; - return this; - } - - /** - * End an array. This method most be called to balance calls to - * array. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endArray() throws JSONException { - return this.end('a', ']'); - } - - /** - * End an object. This method most be called to balance calls to - * object. - * @return this - * @throws JSONException If incorrectly nested. - */ - public JSONWriter endObject() throws JSONException { - return this.end('k', '}'); - } - - /** - * Append a key. The key will be associated with the next value. In an - * object, every value must be preceded by a key. - * @param string A key string. - * @return this - * @throws JSONException If the key is out of place. For example, keys - * do not belong in arrays or if the key is null. - */ - public JSONWriter key(String string) throws JSONException { - if (string == null) { - throw new JSONException("Null key."); - } - if (this.mode == 'k') { - try { - JSONObject topObject = this.stack[this.top - 1]; - // don't use the built in putOnce method to maintain Android support - if(topObject.has(string)) { - throw new JSONException("Duplicate key \"" + string + "\""); - } - topObject.put(string, true); - if (this.comma) { - this.writer.append(','); - } - this.writer.append(JSONObject.quote(string)); - this.writer.append(':'); - this.comma = false; - this.mode = 'o'; - return this; - } catch (IOException e) { - // Android as of API 25 does not support this exception constructor - // however we won't worry about it. If an exception is happening here - // it will just throw a "Method not found" exception instead. - throw new JSONException(e); - } - } - throw new JSONException("Misplaced key."); - } - - - /** - * Begin appending a new object. All keys and values until the balancing - * endObject will be appended to this object. The - * endObject method must be called to mark the object's end. - * @return this - * @throws JSONException If the nesting is too deep, or if the object is - * started in the wrong place (for example as a key or after the end of the - * outermost array or object). - */ - public JSONWriter object() throws JSONException { - if (this.mode == 'i') { - this.mode = 'o'; - } - if (this.mode == 'o' || this.mode == 'a') { - this.append("{"); - this.push(new JSONObject()); - this.comma = false; - return this; - } - throw new JSONException("Misplaced object."); - - } - - - /** - * Pop an array or object scope. - * @param c The scope to close. - * @throws JSONException If nesting is wrong. - */ - private void pop(char c) throws JSONException { - if (this.top <= 0) { - throw new JSONException("Nesting error."); - } - char m = this.stack[this.top - 1] == null ? 'a' : 'k'; - if (m != c) { - throw new JSONException("Nesting error."); - } - this.top -= 1; - this.mode = this.top == 0 - ? 'd' - : this.stack[this.top - 1] == null - ? 'a' - : 'k'; - } - - /** - * Push an array or object scope. - * @param jo The scope to open. - * @throws JSONException If nesting is too deep. - */ - private void push(JSONObject jo) throws JSONException { - if (this.top >= maxdepth) { - throw new JSONException("Nesting too deep."); - } - this.stack[this.top] = jo; - this.mode = jo == null ? 'a' : 'k'; - this.top += 1; - } - /** * Make a JSON text of an Object value. If the object has an * value.toJSONString() method, then that method will be used to produce the @@ -311,14 +113,12 @@ public class JSONWriter { *

* Warning: This method assumes that the data structure is acyclical. * - * @param value - * The value to be serialized. + * @param value The value to be serialized. * @return a printable, displayable, transmittable representation of the - * object, beginning with { (left - * brace) and ending with } (right - * brace). - * @throws JSONException - * If the value is or contains an invalid number. + * object, beginning with { (left + * brace) and ending with } (right + * brace). + * @throws JSONException If the value is or contains an invalid number. */ public static String valueToString(Object value) throws JSONException { if (value == null || value.equals(null)) { @@ -339,7 +139,7 @@ public class JSONWriter { if (value instanceof Number) { // not all Numbers may match actual JSON Numbers. i.e. Fractions or Complex final String numberAsString = JSONObject.numberToString((Number) value); - if(JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { + if (JSONObject.NUMBER_PATTERN.matcher(numberAsString).matches()) { // Close enough to a JSON number that we will return it unquoted return numberAsString; } @@ -362,15 +162,217 @@ public class JSONWriter { if (value.getClass().isArray()) { return new JSONArray(value).toString(); } - if(value instanceof Enum){ - return JSONObject.quote(((Enum)value).name()); + if (value instanceof Enum) { + return JSONObject.quote(((Enum) value).name()); } return JSONObject.quote(value.toString()); } + /** + * Append a value. + * + * @param string A string value. + * @return this + * @throws JSONException If the value is out of sequence. + */ + private JSONWriter append(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null pointer"); + } + if (this.mode == 'o' || this.mode == 'a') { + try { + if (this.comma && this.mode == 'a') { + this.writer.append(','); + } + this.writer.append(string); + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + if (this.mode == 'o') { + this.mode = 'k'; + } + this.comma = true; + return this; + } + throw new JSONException("Value out of sequence."); + } + + /** + * Begin appending a new array. All values until the balancing + * endArray will be appended to this array. The + * endArray method must be called to mark the array's end. + * + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter array() throws JSONException { + if (this.mode == 'i' || this.mode == 'o' || this.mode == 'a') { + this.push(null); + this.append("["); + this.comma = false; + return this; + } + throw new JSONException("Misplaced array."); + } + + /** + * End something. + * + * @param m Mode + * @param c Closing character + * @return this + * @throws JSONException If unbalanced. + */ + private JSONWriter end(char m, char c) throws JSONException { + if (this.mode != m) { + throw new JSONException(m == 'a' + ? "Misplaced endArray." + : "Misplaced endObject."); + } + this.pop(m); + try { + this.writer.append(c); + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + this.comma = true; + return this; + } + + /** + * End an array. This method most be called to balance calls to + * array. + * + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endArray() throws JSONException { + return this.end('a', ']'); + } + + /** + * End an object. This method most be called to balance calls to + * object. + * + * @return this + * @throws JSONException If incorrectly nested. + */ + public JSONWriter endObject() throws JSONException { + return this.end('k', '}'); + } + + /** + * Append a key. The key will be associated with the next value. In an + * object, every value must be preceded by a key. + * + * @param string A key string. + * @return this + * @throws JSONException If the key is out of place. For example, keys + * do not belong in arrays or if the key is null. + */ + public JSONWriter key(String string) throws JSONException { + if (string == null) { + throw new JSONException("Null key."); + } + if (this.mode == 'k') { + try { + JSONObject topObject = this.stack[this.top - 1]; + // don't use the built in putOnce method to maintain Android support + if (topObject.has(string)) { + throw new JSONException("Duplicate key \"" + string + "\""); + } + topObject.put(string, true); + if (this.comma) { + this.writer.append(','); + } + this.writer.append(JSONObject.quote(string)); + this.writer.append(':'); + this.comma = false; + this.mode = 'o'; + return this; + } catch (IOException e) { + // Android as of API 25 does not support this exception constructor + // however we won't worry about it. If an exception is happening here + // it will just throw a "Method not found" exception instead. + throw new JSONException(e); + } + } + throw new JSONException("Misplaced key."); + } + + /** + * Begin appending a new object. All keys and values until the balancing + * endObject will be appended to this object. The + * endObject method must be called to mark the object's end. + * + * @return this + * @throws JSONException If the nesting is too deep, or if the object is + * started in the wrong place (for example as a key or after the end of the + * outermost array or object). + */ + public JSONWriter object() throws JSONException { + if (this.mode == 'i') { + this.mode = 'o'; + } + if (this.mode == 'o' || this.mode == 'a') { + this.append("{"); + this.push(new JSONObject()); + this.comma = false; + return this; + } + throw new JSONException("Misplaced object."); + + } + + /** + * Pop an array or object scope. + * + * @param c The scope to close. + * @throws JSONException If nesting is wrong. + */ + private void pop(char c) throws JSONException { + if (this.top <= 0) { + throw new JSONException("Nesting error."); + } + char m = this.stack[this.top - 1] == null ? 'a' : 'k'; + if (m != c) { + throw new JSONException("Nesting error."); + } + this.top -= 1; + this.mode = this.top == 0 + ? 'd' + : this.stack[this.top - 1] == null + ? 'a' + : 'k'; + } + + /** + * Push an array or object scope. + * + * @param jo The scope to open. + * @throws JSONException If nesting is too deep. + */ + private void push(JSONObject jo) throws JSONException { + if (this.top >= maxdepth) { + throw new JSONException("Nesting too deep."); + } + this.stack[this.top] = jo; + this.mode = jo == null ? 'a' : 'k'; + this.top += 1; + } + /** * Append either the value true or the value * false. + * * @param b A boolean. * @return this * @throws JSONException @@ -381,6 +383,7 @@ public class JSONWriter { /** * Append a double value. + * * @param d A double. * @return this * @throws JSONException If the number is not finite. @@ -391,6 +394,7 @@ public class JSONWriter { /** * Append a long value. + * * @param l A long. * @return this * @throws JSONException @@ -402,8 +406,9 @@ public class JSONWriter { /** * Append an object value. + * * @param object The object to append. It can be null, or a Boolean, Number, - * String, JSONObject, or JSONArray, or an object that implements JSONString. + * String, JSONObject, or JSONArray, or an object that implements JSONString. * @return this * @throws JSONException If the value is out of sequence. */ diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Chat.class b/target/classes/com/io/yutian/livemutually/liveroom/Chat.class index 4ea9b4d021cec26f121f9885ee549fd285ee4262..e7ce687356fce0269abc2cafdea44fdab2784016 100644 GIT binary patch delta 16 YcmaFD_=J(;)W2Q(7#J9ACUV>X06pgh!~g&Q delta 16 YcmaFD_=J(;)W2Q(7#J9ACvw~Y06psl#Q*>R diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Follow.class b/target/classes/com/io/yutian/livemutually/liveroom/Follow.class index c1def0ce7737f0f40e8854a0d9fae81fe94d272d..c553d2485d7f34b203b46183d0f43569170aa423 100644 GIT binary patch delta 16 YcmdnbxSx^Z)W2Q(7#J9ACUR^C06A<1RR910 delta 16 YcmdnbxSx^Z)W2Q(7#J9ACvt2D06B05RsaA1 diff --git a/target/classes/com/io/yutian/livemutually/liveroom/Like.class b/target/classes/com/io/yutian/livemutually/liveroom/Like.class index 9bad31ecadbd25ed119f3eff98eb55a72f025311..f5ae478e7b6777f7092a70cbb9dc042fe19d91ee 100644 GIT binary patch delta 16 Ycmcb}c#)Cg)W2Q(7#J9ACUTqv06UrnivR!s delta 16 Ycmcb}c#)Cg)W2Q(7#J9ACvuzw06U%ri~s-t diff --git a/target/classes/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.class b/target/classes/com/io/yutian/livemutually/liveroom/LiveRoomWatcher.class index e8b3ff2f569767ed7bdfe042156b94fe8bd0cf26..de126572b34dc858ebaaa948480d5f3cfaa0aa23 100644 GIT binary patch delta 147 zcmcaCep#I3)W2Q(7#J9AHgZI9FtSZf=5XfZV_;?AXW(KGn7p1Nol^wJ69w|bCd+fC zb4meu(ml~V^Oq6-w!2a9|J ciWmV!jDaGiK#>?u3n0%D$g>8^@8*^O0B>L$H~;_u delta 147 zcmcaCep#I3)W2Q(7#J9AH*!RAFmg;z=5Xc|U|?ksWZ+^Dn!KJPol^|R69@7nCd+fC zbIJgDvOu2PC}O=gj5QU|?s^1nShDY|kw|xrfV@Qx7Pj4-_#3i+lu% bm;gmgfgz>% diff --git a/target/classes/com/io/yutian/livemutually/manager/KSLiveRoomManager.class b/target/classes/com/io/yutian/livemutually/manager/KSLiveRoomManager.class index 86ac78741ed7d109e8663639623f5ba43d5a0780..dfb5f8a8d389569466957f28b4be63939a567ba1 100644 GIT binary patch delta 215 zcmZ3$Igg9$)W2Q(7#J9A7_299m2$8b42bfbba^gfujLvJ%S@ z^-D`KbM$>Oi%YB-8T2$XC)+XUO%`Cb669jwX5e99WY7kh#>Ak*z&bgaS&NZ#avQTd zCm&FTpMj4-aPl?gcu`RXb_Ov94h9JZAqGhxONv2`L3*+mi;O8JgDHalzV oAi%PTfqesz2HFNB*}$T5P+QD_rdUkQWYOW~28vq(<(U|)09%D5DF6Tf delta 153 zcmbQowSbfB)W2Q(7#J9A87wApmGblDWR|5C*wH3_=XD3}Os& sKq$|k57YN delta 17 Zcmew+|4p9b)W2Q(7#J9AH*&n=2LMYa2W9{O diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouChat.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouChat.class index afb66a6f88cc0d7f63b8759a6064cd144ae0601d..07e8d9d3d913d8d7918f8bfd017929f17572ffb0 100644 GIT binary patch delta 17 Zcmeyz_K%I@)W2Q(7#J9AHgc3P0{}=k2EPCR delta 17 Zcmeyz_K%I@)W2Q(7#J9AH*%CQ0{}=p2EYIS diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouLike.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouLike.class index aca51464fc22d2c553c4a6356b0e7a2f04833a2e..01e8038e6681cf99ab8d22912a24be6e1d81eb5b 100644 GIT binary patch delta 17 ZcmX@Yc7%=N)W2Q(7#J9AHgec70{}pT1`z-N delta 17 ZcmX@Yc7%=N)W2Q(7#J9AH*(l80{}pY1`+@O diff --git a/target/classes/com/io/yutian/livemutually/wss/KuaiShouUser.class b/target/classes/com/io/yutian/livemutually/wss/KuaiShouUser.class index 2fcee99b237f52b5699abc2023a93ea9a92d0fae..4646905d5111bf3c4a221b08b85c8132455f17b0 100644 GIT binary patch delta 17 ZcmaFO@|uO?)W2Q(7#J9AHgY600RTmH27v$o delta 17 ZcmaFO@|uO?)W2Q(7#J9AH*zF10RTmM27&+p diff --git a/target/classes/com/io/yutian/mclive/Main.class b/target/classes/com/io/yutian/mclive/Main.class index b1cea0e3326f2a15ba31d1d594973df66b037545..0173846fa54b4ff79f37ee7b585b2ceb7c2529b1 100644 GIT binary patch literal 13328 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 RFep#YKcc;UP?}CkxyoE$>a%4dsrD6 zEzj8coX_=t>Ix{1d=QaDh#R&43i^StpS}iAY%Xk delta 135 zcmbQjJ%yX&)W2Q(7#J9AH*y#-P0nFj%B-NFIoXg=PLPp-FFm!yBQY-}C$-2Yv$$k3 zKl4XcMg}zo^~w4yDvVN-C$fk#YcOa|&S6oR9L%CKc@K-=|{FjSy_}B^(RNNS_1%|;U6Xd diff --git a/target/classes/com/io/yutian/mclive/event/LiveLikeEvents.class b/target/classes/com/io/yutian/mclive/event/LiveLikeEvents.class index 6e5bfdc77e02d4872352720cc4863aba38af5540..111588ded5686adcd3e87422a8df4547a2dd63ac 100644 GIT binary patch delta 121 zcmaFB`GAw-)W2Q(7#J9AHgbGt8I>o0U>0T80;+UnQ5M!< YkO!L0160qzz`~%wpvb^5c`1uE05OIiegFUf delta 137 zcmaFB`GAw-)W2Q(7#J9AH*$PuoNUb0JXwHAnV&B`wZtPaFC{0n$S1S7WbziKtE`L+ zN({=AcQC6+iZjRqb?`7S0!dZ|1qMYR&B&m_pgQ?0v&!T<%u183S)?aRvM5Y0W08?m ZK~l{ERV~S&#h^L4k6D>fWAaiKYXHWV9mD_t diff --git a/target/classes/com/io/yutian/mclive/event/ZhuboAPI.class b/target/classes/com/io/yutian/mclive/event/ZhuboAPI.class index 8c67c76d022e93e7b4d04c763eb08147e2c7299a..e98bc97482456b2686ffc52a911942d4ca490fea 100644 GIT binary patch delta 148 zcmcb@`;wRA)W2Q(7#J9AHgfD>6yQ$JOe^tC%u7s9En;La@R_X6s4U}-5VPiD&|uJH zXVBtd&}PtKWY7aC;6_rgS%Zn0nUQm{4r?GM9|IQyKLZbg;N&*e21cpL>} z!Jx^`pvA+W&7i}`ppu-QtDl*#Us+m`nV6@ao1Bwbma31YX|pX8GczOiWJlIOPJRY1 z1_1^h2BFEbSsNH7Cd;q|3(7LcGsrQh14&H=MFt%PrO6F!aooHNj0_P#r!g@^0syOz BDii13L=(KORCRIor|VO&B)#Aujyh7pWuM1w&r+QdREZEeNokDW_m z)5JnZ6AKFu-~n9Px9|Y?mzG~nzH{nU-O}HFUS5IOC;dLr-nr-$3uQly7Q8jr3FLFa z(sY`FP&TzNEl4dpTT#I;tp;A`t~4si^H{IRJ0A!>m8Oj^x@i5|H;q{4TMUd)#G=9| zHO4q*oHHi4V3Iqgcwm}GW@O$tjnAQDlVV=p8YiYhTs_m2FmdU_V~{0=@a1s!D!Z;| xK!y#v2^A?TvMF2H>z-{ic1W^|K}4QC`q@|I164khm${m#%HNzVj-=gD{R3y6I-dXl delta 241 zcmYk#JxfAy9L4ePKk{) zrNFf>KE@|q6kobNM3V^L|8lS63;n1mmkhI%n4`)97c6qc5;r7yWSM7HXtOFY`_jLL z$vQzc>YW}G{xpw%P99#QtS9B3&@-?<+@#Ndbk$Jm?@ a==mR8!!-Z^ diff --git a/target/classes/com/io/yutian/verify/PluginVerifyResult.class b/target/classes/com/io/yutian/verify/PluginVerifyResult.class index d483c3da2477277fc6bf5dc1ef607522fe224922..61010ce98a24f1c40e27e6dde718308868a1b846 100644 GIT binary patch delta 17 ZcmaFG^NNS#)W2Q(7#J9AHgY7f0RTov29f{( delta 17 ZcmaFG^NNS#)W2Q(7#J9AH*zGg0RTo!29p2) diff --git a/target/classes/com/io/yutian/verify/VerifyHandler.class b/target/classes/com/io/yutian/verify/VerifyHandler.class index a20f5e72a9233b967b18cad744986a6974ebb1a7..0562ac72c29c84d473d47028efb5518314027b17 100644 GIT binary patch delta 176 zcmWlSI}1Sp9L0aP47V#0dFFcLxfwmLM?{oluo-1BP)rOa1B-IM#oZ)}L0OHyg->9U zzwMmUIlp7+maaa(w>wZdt9wncnrH0{{R3 delta 176 zcmW;Ey9+@99L4dkGTbhT$g}H}M<$#1^~@tB8Ei&b3=|WC$-tucFYYE;49aTsU-%1b z^2>6Db50*aGc@J-z1;!*tn5^i^>kEU13P9MC^&KB!Y>)dLmV$@eB`955TK1lk03)r zjN~yU!jvd;Vl3pnB*B^_8&a36Y{{`?j-+YQ zl%|`Mp1VmF(uUmR;L0T&vMot-_1J87+wI+Mx94`Z+ikbUwn?GQ1f}*S($PK32L_UW+0dXV zQtYoy55yDO+8VoD+{^cDiS?)B$wXU_CW8Fsd-@@uSWhGxnb9&Mk{pQ4xK=@r*fd#$ zX>N{g*;zvMG}WSMHch7)Or$mObN?_i8+Yy+q-&?kVF)A^$9*#+RT3|}Q0m@6|b&@&2T-`03lm@cIG z7F}f10%`-EoE|%py}b|$gar0e@f$$}%%)Q~StuZ3MrK2&hOpSCCA5?&7*EFrqG{1U zRhD99T%FE%TbP#7a*I~jbP275sB-wSQ<$vn(Ns57t`KgJ6{~EzR77Hmsf3`0)i!n0 z8Vu|ni1ss0FDA@l0yF4xo35a>Aaz>|Mi)(`-~?Gxm9?lr$o5Kti(GBfH9}`;PWa9u zE1psg5TXsV(W1Yy=~~*vG%h!BAhuNms2vOH1j4RZVq1E9fwr>BSE9Y%rl^Q^bS#k? z9Ef#9`=eXp>Aiwwi%mTwS^yxX8AVv`ENUcS+Gf*sieqK}AXZK-veM%limNt@c8Yeg z2ID$RkouUG=3G+(N<7*d*(G)wNo`LK_Vz?J$KXgQ<#pmqTU<+$yQsvOnIYOi=a(aJ z_1kmo^@GOd4?R|;ued=-EOk!eRMNqo$QCKE8!uY$R_d zy3?kQNZ<*?ZWxUArY!nsUUtwZgYC3=s}wvqKA<%dXj^%04KmAv@H`^J!;co zdJN$qoz$!q#$H!JPtcPV9kJ;s9m~VCOo0Xb{^&p|mc1(~v}G2Z$mU=bdtCm5^fdIP zDYWR>ya2SKHyK3`e1WFR zV)@?^%l|ghW$$9$dH9rRyo~tp5v29Xiy*oS3)IxT$!$JBt&5ko5 zzVt(zek5f>FrLu%7owlgPc8a)n|?~FNB7>m;aG2oeRMuP*tU5Tj|K_R{{C13)nu#JSe@C!0XXEe37kiTsQKWB z%WbaUu@Ei%GnZ2uIyXo;8s;jS$1y7M5PZ;mES2Aez%7430>?j@R%>$|PlRLvB^&ho z4V}v*tcE#a^JJc)pXxACnAi1a*7%gkJ45CzFKQIiY@W_2yn{gD1TwHmfm|>{Ss0#oO3fPPnTSF>i)pS`Qcmpg#4r_lypqetB0k^d**piK9)({j72lSS z`eDUD67g*8aF~r7EJo4oF+2ej;<5)+s@qWGpY22Y-Rr-kEd^(w?hFRiPi{ffwX zzQ*SDyn)G1$5Il>;yoz33j$QuFJ)K7sQ0(?wM^6U0(o{yt|HOZG<=P(v-x_CB0QyZ z`QGW#ac-R(r<@^+w@7u7t-~F~g?KXp0m#^D^ETeD(qL==1WO<(n49NP4pgk?9X9Wj zEmX-3gIFqtECBm#P6$BpvCHLw#n@j}7XtNIj>Q8ur=;W!48;19yCgmHpv}9asCpd6Y}BMPLaxuU{QB$|%I6Fsp#3nJ$U0S9fq zmp=`a%Alm`Y>3Mt$JW1`OuEJ5L)jV@4?EX*Ok)Q>Aa3^{(|E7`Jf+N4VSbnzEq){$ zDpd^8U8pc0w)ruB98{!}G9=#X2-BUWocl?ekML1wA)SOvqwL6=CSEQM2vbJ2tve1X zzo?D_f;cq3R^oXY8*U~q2Iakix_!8)7a{zb9myUnDea0UVme!`ldJ$NUCAxc-m9Vm zak+N}&Ghzo3f`P^Q5_wac#kY`oy=*C0YOmp##kxHOf|WE-SRe(u@B#<7TT9D@PLy=dcq>Fk9ih$HmY^9hste6Bx`4> zFWk7eBoscd_$S#ITJT73(E#4b$;H5D)Aca$*#{Q?XCZhOTmaPL1}+D(VMsRa-JOA? zePJdW{IhN)Ea&zM62!c-oD`w;r7Io*j#4EMPf3*oWIIqOLFuU&_6xeN=|k1eWTZBO zMUNV?nL*Y2(30H~+ZrA0&F;#jwqO{Ww>7NI2y+>B)s6r_HyCTC?!oUG2X`f?nxa2U3rA-G%VVGx`r+0rBnAv+B50fI-p0??~S zsV6HMHWX2Lw((yOhXhlhbu_<2$QnMoLY1$DFe+5ZI($@$(8AHlUVN(b96qO1YgSP& zQ^02aNPCkCx6=w>so^3xVe!SJ)R$aX64ygcoUK}$6`)(ZDlcm+I1=)ZcbG={4_qXg6O{9?`jE`4i}cA@3VIklK-9vMbyPwl3aQM7O9D86&mZ z4ZWz;G{;VrY8luy>%%~mciha2Ev92?xysKGQf>edQ&pAV2*r&E(EAMMd%HWGGBT8i z#d=a5$-X}9gq*C4xt^*K;<94BG1Ey*=5JDh(J5L&hTG zV#~nU*$u`Lu#>aAcw!e0M;#66GZC#s?C}21QHN5KGf@Lw8BO%`#!^$elF6Ne{oc)i zW85BY@)k+~R%H4{&wNK7UiWZ7bVPf5*JAI}7Bo03>rvp zwIe)@G;kwkpp`H#Z-y-1njB2@;30B8&cp`$BFU|hGzgAx(75!^+SoACN@oH`&Z;(w zr7WWx`}bi|FD6XOK=;AZL&+1LBX>A~mOM>Hlh7dM2Hq_U=y#7!@dwf-cPJVzlt`&m30jTwZ)Ka(~B&R_9!xU=;d&hSax3A_sc z8}K*@yfXh7;FUFJXiVA0LlkN*AD5x(({jJC_MqI?$$f3vwTI|-j80NBTkfYyxoLWq zLQm6_3^iPRm@2X(_->IQnffF!5Ov~DaLK=V{I%e(34hn(Z>57mW*4BmpUkHO=;3qw zpK@jiw>}nbw^B7eW8iixjiPNd7Dsy3w3F(nkET)*@cVH#K7cdp6tz(rH0+|)w42t` zUb+?MZXc%iMN5RY&$YkYQL9wxCQpL#13Yp>%qnG!Vdx0iVSb6N`2zHO$s(pye1d z@MIKJTE4#dB%ODX=HzrL=S)Ngp~rg_B~^N|OUWqy9G`}Tj#13AzVF7Rgl^YbWj%2W3b-GA@3($`7I)wU&K?fgKn4pZhnbB5BgumsJ2xZc?BaP z(edOv2cTM_7m7s3S+wkJyoaGB^s0!GH#ppW8L)-h4#%P|%h0+5)Y?2m>lX?OwHdn3 zx2Uf6L8@wMt{bAwilwbr@AL8Db3Z>!qh6pL>&tsjQsQ*Z%$m@#CdvTtNjTULRnYTz ziR2Vb!I9Z4d|yDHrTO$ZxaUiX)h5iWqw)M2zYcOL2#su9i4r?praSlx5QJv2ip+CZ zU@D_A5O?S;8fTPv6dHnQ-iO%|1iArT1Xw1dPG~*_JExYDlsbViL#YJe0@rW4V#i$m zA}El5W&9;E7IpPy{s&Mcu)poVUXS1);v1u3x155*P-_w3KX4hvWEc1t7x)-{1MCUQ zV_48g+`=m#j+SQQPDlufN;0&!ko2D^=-L*7T4av4{}JnO%I&}z09+Bpgr=Gd-MG31 z)-8nBoPu9NSBcXt0$IljX+9DC2C?+FVEK2jtKWMBG{IHO1b!2uLUg9F{7=Gzvce$o z85(cl&f{fr(BV*W(xIeFDEYwZmZ#~18M@;jRkgfC`w!5VmKSM%`NuN!i38;K9l_(j z9sc+p{n$KGQ+|Ml=-$&V?aIg4L;&$q9b0I)1!)?WP&V`#ypn3r zK@+Qxa8%(K|l7KCo1YJAt>lp{+d(Il6&N&Id8jtUZ!6p;pwAII}| z`FkKh5`|8JGV5P4OF#QQo=M73&RS>T@XU2qCw%-H;@l7ThmQ33K`>(FHIDQ_^H+ou zyQci>Ex7rnWb+LxK*hF2Mqy!1oS-$hk8uWrAxr~lq$r1@}&1m{?&+-)N4xWLCJp?qvTmo z@*F71fRdsAGbR7SzkUyT{MAvd$KM`>9^ZZ^J-!0!UIlfpL62XA9=`-Ve)%t~M;!Hy zC_SWC(c^wd?@Tf2eFOA;6ZCxx()-T;S$e-g%GY(+VuU~`aBS>;B!JwAjD7_7o*E4O zZ;5R>R@rWNu$}P0{9Ag{;p=ONL_=xUAC&VKAha)vwf&CWJ6xz>blL*wbfJuRg5X5ccKTZ<$|?b zHRF95hw?rE7MYg7Vz}~1ckL0C<7A7UP3M~8777@xWEnFRXi4D`QL^t!zvMeU%lu@fxzQH61XhrKG7y((8#RZ!JguxF{N z;0$n8;O`{H%p7~8G<(fRyPCaxBWx@*!kaznD`tdv)5!sp1~M_iau0L0M_MI$yNszE zXxs_LJ_5$}gRze(_+p7z=`vOBGF1+yURO+omCii1m}W&@>x4R_e7puwWaAb@`3O*| zPSgTbdspM2!lm=AvjLRZZcP|eSPf6hK%Suz%+-1F`j*DVmxC(oN2t9Cwd#!vOayGP z)H|>jYdz5{mzQPuyaP0<+I*4cF7Q|T_2?r*JnzH;vsL{m2o(sH3(((bRNqZBM|~62 zH=op#{cC z#rYMme6&drkiQ2rw5XN8i+hvifupbTIb1D}E4Z_0qutd;yDOn~Eg|*vEw^eGiE6mt zSLQAArG6F|Z*jJMo-7;0v4}U7=cvSKOI*pQrwJbmIxKG2Vm7dgHw*)Ndq=4{w%IBh zcDJ?+(RU;eYY$XiEaO##wbwj`G(0IH?t_${DU}5j+kkJFhJR!%Un#`$bzu3%d%*Ie z0xSWPi<*}mcp?T0|5u#_RW634Ts#c5=F*Gi>W9GhBag`r_xS*7a_M~O@T|78BH+bn z`G7kM#oDJ*Ptui@KVWd89`_z zhyp{Ugj}R_62WAYd0B3BA^_5L9`jd0O&0-YzE7NE=B>Lzwohxr;P;A^BZUlW!4TBzFBN)vtO z(KO#|YW2;bS-!b+zVAXsprl6$VUqO4&F8pMnd3@j4t(VdhzvL(LI>T^;=r;633sCk z5dU08w>m5>go2zh02OA2mljH=!y}zyNfK{%I-qU=AR!E80Gy=Tm5$YNm@EkDk({4s zeXjxUm(f_?O8}itMk)bLE6ys^9$ItedQOLh&OOt1qr@R*3+4)6NNNYD1P!*s z6i{8btvM>R`ZszQ^d_=5P(JB~>^L$mg=d}u4%^2d94iZmU;~VfG z@oMZ1)Ju6CJ^&r%0@dOwX{sOMON#*YmJ|6v&lUo8qvk?K2PnXXxVs3@506Nxv|NSh z(Eu(LLXPQ4LwtD=P@fzL6rzL%Ra!(*^`fYC4meN6;k(ZRE>BTL87PvDlMS#+b=Xa0 z6;&=YQ{18XQiuK0<{`dXc4laZHC)}4b1+rf`<^7zcLY)Is0U@KYa6AibFMk4a5#9M zV*wRSHMM!h%^M4i`vnD5S56f!&!6;mwvHupa|Ka3(!9&hymz>cqIir;)cndihc?EnIwC;>bWgbXD z3j0bppM;)JQ%!?7g~sf4im!Vxhg{4d7jp=Bu*JyXQSjmU4t(u%fIn6IV>tXDzX$lJ z@eAN*&kI&M8U_4Xah2ilfBhcdzdj6pwM)M%pNRO+F!(ryls&dKlo8}3`L=-m36A!A z9PO8h_UlFa>wrVsx^^p78JVRt+AQ;6Dswea=4zsh-++^OZ9qoGcyT*%u|{-+rm@!1 z*Blvcs`+%`xsU1zJV+HSjw9BzWOx&(Q{(p|lA^h_^@O-+jd?we1l9N z5?85CleHR^wgg@I4-_OFEvOi3-b4ZOePo$BONkYu+QEislH$sfuQbg{!jRDl3vgI| ze&8V7>c~MlNmY*3j8=A$;T!TxZdJmVcaUw~N#o3q(0KC>4`0Jd7)PnbIMn#kOv3LQ zG)}$__p%+`-;7wt9!8s64ToiT_dyyX!?>2yNgz@O-*=n2jsFI;TFAT` zvb~2&%meV>gH&zaOEuGdLC|zM5rt8edC~7`I zaq~&q1(kVX80EUYQRrDC&?3GYXm`; zL+E48mnmYt0{?#%Qh9|gHeXjjXtiL1YA5-G!h!(g4|^Qd(@rvG8nYl-x6;5hex0LXA|sh- zeh>}$=9%K3^ZdaL0!21%XEByzk zx32d3c>&)%tJ13UKSyGD!2|wfD(Y2v^OOfgwJi;8j`zQ0@g{)q{8aULuq06rc!&WBfsJ|05)K`WMy zQ3%m~HpZYK*SChWMrBM{mDt1(ewl>VaFnsP%bsoq1!OmeA64NWtcs40K8v0ck11J? z>vC9Z32>c*$EX)_WFBVmeR+0*-x+B;;Y!daRNO4D%K^}$$62g z^)(vh?vzJ2^Yi?WK~Q%anh1#1W1G=M2xs`w?07xK5!%&weTJXNj)$9Doy&8Fn#JHf z-A4Z=DDyff@p`(zABFNZ(jd3|u|34Gi=O6$8 literal 18677 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 UV=6^?)=cVeRa>*IxIgpPb5-1YIz{e27AjuFr SS(_!2jUfZb&79oDVg~>`kq=7% diff --git a/target/classes/json/JSONPointer.class b/target/classes/json/JSONPointer.class index 6852361799a98aa498239889aa2768756f4098d5..73e5045b876a53aed3c80a6b7a20101ff88d24f1 100644 GIT binary patch delta 2914 zcmZuz2~7vWehsuq-tgkiGbJvNqp_sxhoZH~yh@819Z z%lCc%|2X45(L-RJG~1$(?LU0eL!Apu)$AQ%YM z2_zMlw5hNUZ!9c!2;^+*3j{;{{`_uVFxVH$@ABsd!@a!@sL-UdRDtYbL)bFY+8jtn zhKgw#bYu=cKQ}#FKorW|=^AcA?(k$`%1q2s;na|a*+!{Ylv02>Dhf5+jJd{uSddX9 zU@7#wp;^WKMu{y?eA1{?YwSDlfQC=uL1R#zOYA?R;j`Fl9I>rV9fBt9 zKd0gI_=0g<&0q0F0e#Bh${pSQZJ|J4P^aj=q~T$B1*YHa+v;E57wqFyL8keoj#42jsVrwyW z+6#nc9JDX67hpoeMIwFKexIl`)+O&bBOKF(aXGof*pt#-sdk6^`~5+(r@xP=c&tSN z4pSlP`nLLme3=4bz{p8imaOz|_w|ysMq|i6-N@29#lvU9nqNc;*p=Y_8TdYa0D&Kx zZ}Ztd><7NTXuiLMWUkq2{QWY`ok)U>CvUi75bkG?Iu5l%PmUpF!W~80erRqzD~g;E zq`1Z~;~er&x%GKb6pz5^wnpJ;^62x&@SC`y{gQPWWxL+ylHuQzQpw<2vurQf9; z`tlf7PPoRgsw0M5=*v=;NM7Ca!E}kYC^mT?{hpYg{P|2vkCwWQi6P z1uA_ZU7h40|j@QfT87#N2_KIN1{B>zIJt&U5L0z20~`BQGN9)|nBo7nyj7O|lP8t|bfc z_<1JIpeOcqyjn4vA0{g;I^AQqcd_dtb~Vo`jUsXcbH*{$p?|{qZCE;z+^rpo+#bW8 z)-ilKhKD9fXGO8^WQ`)tW?ewU0CJTW9+_w|FCI`57isR1+swFJetIy>a23?y2-gkf zGY)DQKLo&jrgMNP9%M0xP>4sV(?h&@gw~H!Ku3Aw7!`cnEUb>5T}=vJLF6~A-Ab*! zMLAgLpdP=)?-+Cqa(MiE6W>n!0e@r=){D1IRY}=!KwLvPwXtiZis4D}LCx+Te-u4>SRas&d-Og&B##!8#~c&qv*h9&`H7OB z7$cwk0Dc7ijK3uKQ4;)w+dLxQkb-6!2m@%KjsnoG~)ae>GPTDck(%rU)2@m#`* z5oCHU;zX{(`4Po8IHJn9y=w%i9=Ugl8%~p}b8(VABso{P&hO~tlOCD<`^otZv;QvA z@GK$!9>2xUVIiL9czFSh5zgc`PULMYc7R1m*|~^Y8J3x{lfn#7HAfhKB@z6?isM64 zVln$S7q+x(NHM`b`W{@QAmb`>uk!c?34$3slOW+Rvy+Y&k0TQ6(AUOrzHtheCXWfs z2*>GlT$JP&b99A7zlI{Fw2;%M3U8XlMsfejKdX@SW_{UVbFDKG%7zeY>`rvHy|1Vql^?5d) z?-euOM)?uqRH~hzd!UY5uZqyO907S042Y?Ns1{M=m4vQ_f~u#amJs#pyc7}pcqtB+ z$pYwVNn%h-rwxhnWKA<=gG|*lcT%pIpdJ=`!|ZPfzbcfe=U* K7NH0ma{mjzRd;#- delta 2905 zcmZuzYgAO%75>g};SQH8GhXDS%!q;_GlNrnAs}dcG#v|Qiq+8C%0xyoibD#6QB$3? zjY(T#6MD@9HC59@QezqvGlQ}kbTzB#>gp@@5tAl0={u=ynl$MTS1kR`onhLwnqjSb z&e>=0efIwLx6fh3JK}Zz=-(Hf2hbo?6*hq)^M+Pbqd`E?k%=sUtYzUyINB(XF|Tr? z20IiL*#ge(!CgUrS1{7)Uw7N~(AKDmoPlL!V#+}K47&!0w04>&L}4zC4CJYprXwE( z15eFy6bXnjDV?rk21*8AE6Z_XrixM>voPD#M14*<=BSvfqXP5H^^{@lw*sIb_C5U8B6>DY?bkM2o%ptYFj>6b$4~K z2;uHXedR_w7^H1~HQk%-$^nNAB~O0#=-7+9Oii7evk&`K#B|(^0<&J7W#5M{>-Y-p zF+0>!*S!Lc?Y-R*|Jv4dEy?m~IDoI}_!_=$o>2X675B@WCUtJ==?U(Uk$ywR19;GU zRc)EA;+qUIt+Kv59EpZ{*6iLIx+abZBsjI^EL)zb zO_^ETI*Gyn92$<`sEWsQd<)0S_mmnEtsl48@wnNMS!X|jCv-fCljh0HIre@$rQu z4~bYg8xXKzI(~wmvP*wvtrikPb_&-&x7IHqn}3(_3rc=TNhvMl&prmxmO#cZ=K{at zH;W^%k6*Scztpz5kU=@{h9?2l`y!@{L2EPg1aik?-Z-WnhVC^A<0u|Pj%O6E=TLUW zYgESJ9fZr98Hc~wXDl4W;^rY#wv3^nttx@#<6dK>rJ{QksWK$}u5L3vp1>!@J!1$k zuusyJtubSX;!@g|C_bk@pQ=rb`HJfp^nAq^k750B7@l(|Gd7&ZMjJK_q1JT-ZXb2x z_-tLK+$+u;#pfrtr*n(v{oju3=)_}Zm~kyuU=`|_v&3UB-2C=K!Tm(~AaQ*Fv+*D* z$pa4##jp?$(c}>t9j4i%SdBq6lb0KD6kEv4FpeXN6WE2vu?J6JKN)!+PU2yb@kyNG z_i3EPXL+C!b01ynX6Y2lhdE6r<8cT5)0 z;2FU4$iq0BN%*^k>-j?BO zZBvSs)=@l^z~K1M#3`R_#Uw`a`DB2F)7jLy%-hKZp3fFu%yid~oK4JQBUy0=%iPNn z%d9-ic0N{GSt&7@L>kUM{26~?qE3YH7H2lA;>F0yy@4DRb`|?RgojmiUPtkDZof@) zg$4t}nRj>!otY_g+P$MV+~B#0{+5!eIF6sdY%8%7$;7-&EGFfn`1WLaRV8r@4c94B zujB&xj$$Q&;qhk6@Hxdo#4ELZQcM`C<YwT4FQ<|ZSjiWDbn>O?e;gNLRr1~VVH}q> z4Nbhxx8{Z6Ci#BwK3P6)h26-qP2SaRQWIhZfhr+TF4PD&>cmVe7o}JsW~CdXg5^&F zrm>llFs(>ku@Z_v3dA?D0Q`THwN7I7ww1JAv6Z2$lO diff --git a/target/classes/json/JSONPointerException.class b/target/classes/json/JSONPointerException.class index f7d9c662acc1d977a741e57e5f701483aa7a430b..9d0242e5cfec9c2b5a1550420bac2ce6e997a163 100644 GIT binary patch delta 17 Zcmcc4a-D_a)W2Q(7#J9AHgfng0RTeH21Nh> delta 17 Zcmcc4a-D_a)W2Q(7#J9AH*)wh0RTeM21Wn? diff --git a/target/classes/json/JSONPropertyIgnore.class b/target/classes/json/JSONPropertyIgnore.class index 5e092d59044eddfcd3f22858358f34bfef491827..5e2afb6026263ac907e35a01e960110453beb4ea 100644 GIT binary patch delta 17 ZcmdnQyos6P)W2Q(7#J9AHgc?D1OPt722%h4 delta 17 ZcmdnQyos6P)W2Q(7#J9AH*&0E1OPtC22=n5 diff --git a/target/classes/json/JSONPropertyName.class b/target/classes/json/JSONPropertyName.class index 6fae440bbd933b300f6046ffbb749867f03bfb08..fad21d575747bbed69c4a940df3151afaef10846 100644 GIT binary patch delta 17 ZcmX@he3qHx)W2Q(7#J9AHgX(g1OP%>2BZK0 delta 17 ZcmX@he3qHx)W2Q(7#J9AH*y?h1OP%`2BiQ1 diff --git a/target/classes/json/JSONString.class b/target/classes/json/JSONString.class index 2ca4132f342390a79da2d38a120ef4c4682d1f4d..45cd76d755901018a52c859866ac82791ba452a9 100644 GIT binary patch delta 16 YcmbQiID?Vn)W2Q(7#J9ACUQ&w05r1&-~a#s delta 16 YcmbQiID?Vn)W2Q(7#J9ACvr>x05rD+;Q#;t diff --git a/target/classes/json/JSONStringer.class b/target/classes/json/JSONStringer.class index ab5b7d54323e2892b2f736cd67bddcadd7faa0e8..f797072285f977ec440a0e6d2a17c31440be7d67 100644 GIT binary patch delta 33 pcmcb^a)*WE)W2Q(7#J9AHgeo#WOZcVW^kJPjM0bDf3h)?CIHSC3UUAd delta 33 pcmcb^a)*WE)W2Q(7#J9AH*(x$WOZQRW^kPRjM0bDcd{{)CIHR|3U2@a diff --git a/target/classes/json/JSONTokener.class b/target/classes/json/JSONTokener.class index 5ca4c5537218411a44a0093dddbaa40dd1957ab6..5b426bfd45821beb80374795694caef2b93c006e 100644 GIT binary patch delta 1158 zcmYk5e@v8h9LHauhdb|%?%)nC;5ZP35Dy|l1rY?N=m-rm#b7a;crYf{8Lq~r=BAc#bHh5pHmN*RDM(m|p4Sp?TDxK8n$_#OK6ExmIpRnl z5lp5#WNO?S^Qy2>jhlP$vXCJ6lFlNsSWFHzOlAqwxQ`jsQbwH_a<|$W^z?GG!BcHt zrMrjBr=C>1i!b=n#KqPWKB~_);iHvQ+Q?utckmcf*}`Gf>PGZhK%c&WBf9KF`Mh59 zgQt4xHkGiQWOgu~b~1T}Np$=V7UC0nZ^CR?UKjnQF=k!hd7{}#3NJ8`UF5TyVqRn} zFPqCTkv8Qz2H>7b|@n;BemORceh4UKM1&!-FX7PPQ`!u!aBT6j~ zsQHx9oq5Zb8u;&iGy*p^|9l4(Be*15Jzey*SN@{4{7MYJ>8#&1kIU+9P@}w}{8uUE znriz)OB>Q+{-S~Fky!~?%D4PxPNTa%(;Zj8K*nJg2hrjrMqDI|n=FZmK+;tZWRF3z zBi)FK&fugjE7Fn!YeZqA>XyZavx3+p1&5?@i=^X`4B{k{fZRs9WRWe|8J1&-)hiE{HTAkCc!6t=81$dm3=UwIrk~mvt9K(O@{1XqO_AE9ZV6U`kssM zvsupYxSVCHoTEd|vr{gZ)qzB1d@?Xc z89xu~nm4G7SG1I?8uT?S>`&5Uh)nrgm0xGFjHr%(D3KeK>X$Od66RYrsw|6YD~ejn qPQ5kGbSCF2>xtxSy^bW8DtB?P%(A?uEm$K#b1b-2vP@pe-Twk}R|ix8 delta 1156 zcmYk4eN5G56vsaY?(K5nUhchc11=Zj3Pau`Lje&4g$oiSis@!*ZPJaxTv`YQYdS*B&$O*szixwIf4sS+xg*rl)GAoAgE@7Agbp5#YKb({ z9og165=b;l93A;{u~3eK3f#=Y&wK(@l1UZ01erlKh14*I1sYLnMjf5j#Twmc+MJ85 zEA+I|oOGs|w%CSQ&8qMO$+R$qHDvG<*{tQ5`g0(<=Wv{U{hrWeA;N8E!2A{4>U)-G zT1jC8(`ciBc1qc(bvw*v*A%Orvz#-xoXd*0sLo4xc$rjQA)VK_k1l4h^?x;r7}P&^ zYgmc&xZUP`*Bmis$hEWR18nTT&Q2Bi7%#iHmyfjFz1nKGw)%+{-%kmjQN{u0@+r0S zm;>%I88ElqopoPoexI5+qPa)aa6f5UI-qUkaFTpZF_Y7ZYd~?GRhZ|(#tN}&y+}27 zK{2FEoXkE?s_*W70)_*Nx86qWIChCvD8rs@bwi5iBC%Z3T9*~*cM9iOJ-!=!w&y>5W_UAa~Tn@5=cPmHbm@G^!K+JDd_wLy$F@ zk`*Q>8lTR$x+22!vJo*(a@gJK_)z9OSj2`+?=cdCQ=G&}EIx4&5I0#8N4|K?&G?{_ zo9?ajr(u-a?l3Mztj((}a}UQmCV!aBK{ z?IcgzSeiInvtCbJ>)WbXZ)nz=WXd*j82zpXt6j{pDw diff --git a/target/classes/json/JSONWriter.class b/target/classes/json/JSONWriter.class index ffb752bd00e255faca9599b09ce79f7062dd3540..9afad21c139c90e2cbb451f3fbcde018e3cd30f3 100644 GIT binary patch literal 6172 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 literal 6172 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 R-$kOBbAFCWgirV}{=eEWvq1m=