V1.0
This commit is contained in:
commit
58291a6311
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
/out/
|
||||
/.idea/
|
||||
/target/
|
35
pom.xml
Normal file
35
pom.xml
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.example</groupId>
|
||||
<artifactId>GameRanking</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
</properties>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>public-rpg</id>
|
||||
<url>https://repo.aurora-pixels.com/repository/public/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.spigotmc</groupId>
|
||||
<artifactId>spigot-api</artifactId>
|
||||
<version>1.18.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.md5.bungee</groupId>
|
||||
<artifactId>BungeeCord</artifactId>
|
||||
<version>1.12.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
115
src/main/java/com/yaohun/gameranking/GameRanking.java
Normal file
115
src/main/java/com/yaohun/gameranking/GameRanking.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
package com.yaohun.gameranking;
|
||||
|
||||
import com.io.yutian.pixelliveplugin.PixelLiveAPI;
|
||||
import com.yaohun.gameranking.api.RankingAPI;
|
||||
import com.yaohun.gameranking.data.UserData;
|
||||
import com.yaohun.gameranking.manager.RankManager;
|
||||
import com.yaohun.gameranking.manager.UserManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
public class GameRanking extends JavaPlugin {
|
||||
|
||||
private static GameRanking instance;
|
||||
private static RankManager rankManager;
|
||||
private static UserManager userManager;
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
instance = this;
|
||||
saveDefaultConfig();
|
||||
rankManager = new RankManager(getConfig());
|
||||
userManager = new UserManager();
|
||||
//Bukkit.getPluginManager().registerEvents(new KeepEntitiesOnDeath(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
rankManager.SaveRankingData(getConfig());
|
||||
userManager.SaveUserData();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
|
||||
if (label.equalsIgnoreCase("gamerank")) {
|
||||
if(args.length == 0){
|
||||
sender.sendMessage("/gamerank [类型] [时间] - 查看排行榜");
|
||||
return true;
|
||||
}
|
||||
if(args.length == 1 && args[0].equalsIgnoreCase("save")){
|
||||
userManager.SaveUserData();
|
||||
rankManager.SaveRankingData(getConfig());
|
||||
sender.sendMessage("数据保存成功.");
|
||||
return true;
|
||||
}
|
||||
if(args.length == 1 && args[0].equalsIgnoreCase("show")){
|
||||
Player player = (Player) sender;
|
||||
PixelLiveAPI.enableRankHud(player);
|
||||
sender.sendMessage("排行HUD显示: 开启");
|
||||
return true;
|
||||
}
|
||||
if(args.length == 1 && args[0].equalsIgnoreCase("hide")){
|
||||
Player player = (Player) sender;
|
||||
PixelLiveAPI.disableRankHud(player);
|
||||
sender.sendMessage("排行HUD显示: 关闭");
|
||||
return true;
|
||||
}
|
||||
if(args.length == 1 && args[0].equalsIgnoreCase("test")){
|
||||
Player player = (Player) sender;
|
||||
PixelLiveAPI.setRankData(player,1,"https://p3.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813c001_okAAZknl8ANJHBehA90kACfAQOSIQNAbgmgkYD.jpeg?from=3067671334",33);
|
||||
PixelLiveAPI.setRankData(player,2,"https://p26.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-i-0813_owAAbniIEAgz8HcfNXiCiHDQMH4YzAAdBCSeA3.jpeg?from=3067671334",26);
|
||||
PixelLiveAPI.setRankData(player,3,"https://p26.douyinpic.com/aweme/100x100/aweme-avatar/tos-cn-avt-0015_92c143b72684a5f65ab27987a9985fc9.jpeg?from=3067671334",13);
|
||||
sender.sendMessage("排行数据设置成功");
|
||||
return true;
|
||||
}
|
||||
if(args.length >= 1){
|
||||
String dataKey = args[0];
|
||||
String timeKey = rankManager.getTime("yyyy-MM-dd");
|
||||
if(args.length == 2) {
|
||||
timeKey = args[1];
|
||||
}
|
||||
LinkedHashMap<String ,Integer> linkedHashMap;
|
||||
if(timeKey.equalsIgnoreCase("Week")){
|
||||
linkedHashMap = RankingAPI.getRankingWeeklyData(dataKey);
|
||||
} else {
|
||||
linkedHashMap = RankingAPI.getRankingData(timeKey, dataKey);
|
||||
}
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage(" ");
|
||||
sender.sendMessage("§e§l★ §a保卫小麦-"+dataKey+"排行榜 §7(实时更新数据)");
|
||||
int rank = 1;
|
||||
for (String name : linkedHashMap.keySet()){
|
||||
if(rank > 10){break;}
|
||||
sender.sendMessage("§a§l★ §7第 §e" + rank + " §7名: §e" + name + " §7( §6" + linkedHashMap.get(name)+"分 §7)");
|
||||
rank++;
|
||||
}
|
||||
sender.sendMessage(" ");
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static GameRanking inst() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public static RankManager getRankManager() {
|
||||
return rankManager;
|
||||
}
|
||||
|
||||
public static UserManager getUserManager() {
|
||||
return userManager;
|
||||
}
|
||||
}
|
99
src/main/java/com/yaohun/gameranking/api/RankingAPI.java
Normal file
99
src/main/java/com/yaohun/gameranking/api/RankingAPI.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
package com.yaohun.gameranking.api;
|
||||
|
||||
import com.yaohun.gameranking.GameRanking;
|
||||
import com.yaohun.gameranking.data.UserData;
|
||||
import com.yaohun.gameranking.data.UserJson;
|
||||
import com.yaohun.gameranking.manager.RankManager;
|
||||
import com.yaohun.gameranking.manager.UserManager;
|
||||
import jsonrank.JSONObject;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
|
||||
public class RankingAPI {
|
||||
|
||||
public static LinkedHashMap<String,Integer> getRankingData(String timeKey,String dataKey){
|
||||
RankManager rankManager = GameRanking.getRankManager();
|
||||
HashMap<String,Integer> integerHashMap = new HashMap<>();
|
||||
List<UserData> userDataList = rankManager.getUserDataList(timeKey);
|
||||
for (UserData userData : userDataList){
|
||||
int points = userData.getPoints(dataKey);
|
||||
integerHashMap.put(userData.getUserName(),points);
|
||||
}
|
||||
// 将linkedHashMap的条目转换为列表
|
||||
List<Map.Entry<String, Integer>> list = new ArrayList<>(integerHashMap.entrySet());
|
||||
// 按值进行降序排序
|
||||
list.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
|
||||
// 创建一个新的LinkedHashMap并将排序后的条目插入其中
|
||||
LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Integer> entry : list) {
|
||||
sortedMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
public static LinkedHashMap<String,Integer> getRankingWeeklyData(String dataKey){
|
||||
RankManager rankManager = GameRanking.getRankManager();
|
||||
HashMap<String,Integer> integerHashMap = new HashMap<>();
|
||||
LocalDate today = LocalDate.now();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
List<String> dateList = new ArrayList<>();
|
||||
for (int i = 0; i < 7; i++) {
|
||||
LocalDate date = today.minusDays(i);
|
||||
String formattedDate = date.format(formatter);
|
||||
dateList.add(formattedDate);
|
||||
}
|
||||
for (String timeKey : dateList) {
|
||||
List<UserData> userDataList = rankManager.getUserDataList(timeKey);
|
||||
for (UserData userData : userDataList) {
|
||||
String userName = userData.getUserName();
|
||||
int points = userData.getPoints(dataKey);
|
||||
if(integerHashMap.get(userName) == null){
|
||||
integerHashMap.put(userName, points);
|
||||
} else {
|
||||
int nowAmount = integerHashMap.get(userName);
|
||||
integerHashMap.put(userName, (nowAmount + points));
|
||||
}
|
||||
}
|
||||
}
|
||||
// 将linkedHashMap的条目转换为列表
|
||||
List<Map.Entry<String, Integer>> list = new ArrayList<>(integerHashMap.entrySet());
|
||||
// 按值进行降序排序
|
||||
list.sort((entry1, entry2) -> entry2.getValue().compareTo(entry1.getValue()));
|
||||
// 创建一个新的LinkedHashMap并将排序后的条目插入其中
|
||||
LinkedHashMap<String, Integer> sortedMap = new LinkedHashMap<>();
|
||||
for (Map.Entry<String, Integer> entry : list) {
|
||||
sortedMap.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return sortedMap;
|
||||
}
|
||||
|
||||
|
||||
// 向今日某个数据增加数值
|
||||
public static void addUserValue(String userName,String valueKey,int points){
|
||||
RankManager manager = GameRanking.getRankManager();
|
||||
String timeKey = manager.getTime("yyyy-MM-dd");
|
||||
if(manager.isUserDataExit(timeKey,userName)){
|
||||
UserData userData = manager.getUserData(timeKey,userName);
|
||||
userData.addPoints(valueKey,points);
|
||||
} else {
|
||||
List<UserData> userDataList = manager.getUserDataList(timeKey);
|
||||
UserData userData = new UserData(userName);
|
||||
userDataList.add(userData);
|
||||
userData.addPoints(valueKey,points);
|
||||
}
|
||||
}
|
||||
|
||||
// 导入观众数据
|
||||
public static void importAudienceData(String dataObjectString, String userName){
|
||||
UserManager manager = GameRanking.getUserManager();
|
||||
if(GameRanking.getUserManager().isUserJsonDataExit(userName)){
|
||||
UserJson userJson = manager.getUserJsonData(userName);
|
||||
JSONObject dataObject = new JSONObject(dataObjectString);
|
||||
userJson.setDataObject(dataObject);
|
||||
} else {
|
||||
manager.setUserJsonData(userName,new UserJson(dataObjectString));
|
||||
}
|
||||
}
|
||||
}
|
63
src/main/java/com/yaohun/gameranking/data/UserData.java
Normal file
63
src/main/java/com/yaohun/gameranking/data/UserData.java
Normal file
|
@ -0,0 +1,63 @@
|
|||
package com.yaohun.gameranking.data;
|
||||
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class UserData {
|
||||
|
||||
private String userName;
|
||||
|
||||
private HashMap<String ,Integer> pointsMap;
|
||||
|
||||
public UserData(ConfigurationSection section,String userName){
|
||||
this.userName = userName;
|
||||
HashMap<String ,Integer> hashMap = new HashMap<>();
|
||||
for (String key : section.getKeys(false)){
|
||||
hashMap.put(key,section.getInt(key));
|
||||
}
|
||||
this.pointsMap = hashMap;
|
||||
}
|
||||
public UserData(String userName){
|
||||
this.userName = userName;
|
||||
this.pointsMap = new HashMap<>();
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public int getPoints(String key){
|
||||
if(this.pointsMap.get(key) == null){
|
||||
return 0;
|
||||
}
|
||||
return this.pointsMap.get(key);
|
||||
}
|
||||
|
||||
public void setPoints(String key,int amount) {
|
||||
this.pointsMap.put(key,amount);
|
||||
}
|
||||
public void addPoints(String key,int amount) {
|
||||
int nowAmount = getPoints(key) + amount;
|
||||
if(nowAmount >= 2000000000){
|
||||
nowAmount = 2000000000;
|
||||
}
|
||||
setPoints(key,nowAmount + amount);
|
||||
}
|
||||
public void takePoints(String key,int amount) {
|
||||
int nowAmount = getPoints(key) - amount;
|
||||
if(nowAmount <= 0){
|
||||
nowAmount = 0;
|
||||
}
|
||||
setPoints(key,nowAmount);
|
||||
}
|
||||
|
||||
public HashMap<String, Integer> getPointsMap() {
|
||||
return pointsMap;
|
||||
}
|
||||
|
||||
public void setPointsMap(HashMap<String, Integer> pointsMap) {
|
||||
this.pointsMap = pointsMap;
|
||||
}
|
||||
}
|
59
src/main/java/com/yaohun/gameranking/data/UserJson.java
Normal file
59
src/main/java/com/yaohun/gameranking/data/UserJson.java
Normal file
|
@ -0,0 +1,59 @@
|
|||
package com.yaohun.gameranking.data;
|
||||
|
||||
import jsonrank.JSONArray;
|
||||
import jsonrank.JSONObject;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
public class UserJson {
|
||||
|
||||
private String secUid;
|
||||
private String nickName;
|
||||
private String shortId;
|
||||
private String avatarUrl;
|
||||
private JSONObject dataObject;
|
||||
|
||||
public UserJson(String jsonObjectString){
|
||||
this.dataObject = new JSONObject(jsonObjectString);
|
||||
this.secUid = dataObject.getString("secUid");
|
||||
this.nickName = dataObject.getString("nickName");
|
||||
this.shortId = dataObject.getString("shortId");
|
||||
JSONArray jsonArray = dataObject.getJSONObject("AvatarThumb").getJSONArray("urlListList");
|
||||
this.avatarUrl = jsonArray.getString(0);
|
||||
}
|
||||
|
||||
public UserJson(FileConfiguration yml,String nickName){
|
||||
this.nickName = nickName;
|
||||
this.secUid = yml.getString(nickName+".secUid");
|
||||
this.shortId = yml.getString(nickName+".shortId");
|
||||
this.avatarUrl = yml.getString(nickName+".avatarUrl");
|
||||
String dataObjectString = yml.getString(nickName+".dataObject");
|
||||
this.dataObject = new JSONObject(dataObjectString);
|
||||
}
|
||||
|
||||
public String getSecUid() {
|
||||
return secUid;
|
||||
}
|
||||
|
||||
public String getNickName() {
|
||||
return nickName;
|
||||
}
|
||||
|
||||
public String getShortId() {
|
||||
return shortId;
|
||||
}
|
||||
|
||||
public String getAvatarUrl() {
|
||||
return avatarUrl;
|
||||
}
|
||||
|
||||
public JSONObject getDataObject() {
|
||||
return dataObject;
|
||||
}
|
||||
|
||||
public void setDataObject(JSONObject dataObject) {
|
||||
this.dataObject = dataObject;
|
||||
if(!dataObject.getString("nickName").equalsIgnoreCase(nickName)){
|
||||
this.nickName = dataObject.getString("nickName");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
package com.yaohun.gameranking.manager;
|
||||
|
||||
import com.yaohun.gameranking.GameRanking;
|
||||
import com.yaohun.gameranking.data.UserData;
|
||||
import com.yaohun.gameranking.data.UserJson;
|
||||
import org.bukkit.block.data.type.Farmland;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class RankManager {
|
||||
private HashMap<String, List<UserData>> timeUserMap = new HashMap<>();
|
||||
public RankManager(FileConfiguration yml){
|
||||
for (String time : yml.getKeys(false)){
|
||||
List<UserData> userDataList = new ArrayList<>();
|
||||
ConfigurationSection section = yml.getConfigurationSection(time);
|
||||
if(section == null) {continue;}
|
||||
for (String userName : section.getKeys(false)) {
|
||||
ConfigurationSection section1 = section.getConfigurationSection(userName);
|
||||
if(section1 == null) {continue;}
|
||||
userDataList.add(new UserData(section1,userName));
|
||||
}
|
||||
timeUserMap.put(time,userDataList);
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveRankingData(FileConfiguration yml){
|
||||
String timeKey = getTime("yyyy-MM-dd");
|
||||
List<UserData> userDataList = getUserDataList(timeKey);
|
||||
for (UserData userData : userDataList) {
|
||||
HashMap<String, Integer> hashMap = userData.getPointsMap();
|
||||
for (String key : hashMap.keySet()) {
|
||||
yml.set(timeKey + "." + userData.getUserName() + "." +key,hashMap.get(key));
|
||||
}
|
||||
}
|
||||
GameRanking.inst().saveConfig();
|
||||
}
|
||||
|
||||
public boolean isUserDataExit(String timeKey,String userName){
|
||||
List<UserData> userDataList = getUserDataList(timeKey);
|
||||
for (UserData userData : userDataList){
|
||||
if(userData.getUserName().equalsIgnoreCase(userName)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public UserData getUserData(String timeKey,String userName){
|
||||
List<UserData> userDataList = getUserDataList(timeKey);
|
||||
for (UserData userData : userDataList){
|
||||
if(userData.getUserName().equalsIgnoreCase(userName)){
|
||||
return userData;
|
||||
}
|
||||
}
|
||||
return new UserData(userName);
|
||||
}
|
||||
|
||||
public List<UserData> getUserDataList(String timeKey){
|
||||
if(this.timeUserMap.get(timeKey) == null){
|
||||
this.timeUserMap.put(timeKey,new ArrayList<>());
|
||||
return this.timeUserMap.get(timeKey);
|
||||
}
|
||||
return this.timeUserMap.get(timeKey);
|
||||
}
|
||||
|
||||
public HashMap<String, List<UserData>> getTimeUserMap() {
|
||||
return timeUserMap;
|
||||
}
|
||||
|
||||
public String getTime(String format){
|
||||
Date date = Calendar.getInstance().getTime();
|
||||
SimpleDateFormat datatime = new SimpleDateFormat(format);
|
||||
return datatime.format(date);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package com.yaohun.gameranking.manager;
|
||||
|
||||
import com.yaohun.gameranking.GameRanking;
|
||||
import com.yaohun.gameranking.data.UserData;
|
||||
import com.yaohun.gameranking.data.UserJson;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
|
||||
public class UserManager {
|
||||
private File manageFile;
|
||||
private FileConfiguration manageConfig;
|
||||
private HashMap<String, UserJson> userJsonMap = new HashMap<>();
|
||||
|
||||
public UserManager(){
|
||||
manageFile = new File(GameRanking.inst().getDataFolder(),"user.yml");
|
||||
manageConfig = YamlConfiguration.loadConfiguration(manageFile);
|
||||
for (String nickName : manageConfig.getKeys(false)){
|
||||
this.userJsonMap.put(nickName,new UserJson(manageConfig,nickName));
|
||||
}
|
||||
}
|
||||
|
||||
public void SaveUserData(){
|
||||
for (String nickName : userJsonMap.keySet()){
|
||||
UserJson userJson = userJsonMap.get(nickName);
|
||||
manageConfig.set(nickName+".secUid",userJson.getSecUid());
|
||||
manageConfig.set(nickName+".shortId",userJson.getShortId());
|
||||
manageConfig.set(nickName+".avatarUrl",userJson.getAvatarUrl());
|
||||
manageConfig.set(nickName+".dataObject",userJson.getDataObject().toString());
|
||||
}
|
||||
try {
|
||||
manageConfig.save(manageFile);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUserJsonDataExit(String nickName){
|
||||
if(this.userJsonMap.get(nickName) == null){
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public UserJson getUserJsonData(String nickName){
|
||||
if(isUserJsonDataExit(nickName)){
|
||||
return this.userJsonMap.get(nickName);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void setUserJsonData(String nickName,UserJson userJson) {
|
||||
this.userJsonMap.put(nickName,userJson);
|
||||
}
|
||||
|
||||
public HashMap<String, UserJson> getUserJsonMap() {
|
||||
return userJsonMap;
|
||||
}
|
||||
}
|
1517
src/main/java/jsonrank/JSONArray.java
Normal file
1517
src/main/java/jsonrank/JSONArray.java
Normal file
File diff suppressed because it is too large
Load Diff
45
src/main/java/jsonrank/JSONException.java
Normal file
45
src/main/java/jsonrank/JSONException.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package jsonrank;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes when things are amiss.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2015-12-09
|
||||
*/
|
||||
public class JSONException extends RuntimeException {
|
||||
/** Serialization ID */
|
||||
private static final long serialVersionUID = 0;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message
|
||||
* Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(final String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message and 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new JSONException with the specified cause.
|
||||
*
|
||||
* @param cause
|
||||
* The cause.
|
||||
*/
|
||||
public JSONException(final Throwable cause) {
|
||||
super(cause.getMessage(), cause);
|
||||
}
|
||||
|
||||
}
|
2647
src/main/java/jsonrank/JSONObject.java
Normal file
2647
src/main/java/jsonrank/JSONObject.java
Normal file
File diff suppressed because it is too large
Load Diff
293
src/main/java/jsonrank/JSONPointer.java
Normal file
293
src/main/java/jsonrank/JSONPointer.java
Normal file
|
@ -0,0 +1,293 @@
|
|||
package jsonrank;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static java.lang.String.format;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* A JSON Pointer is a simple query language defined for JSON documents by
|
||||
* <a href="https://tools.ietf.org/html/rfc6901">RFC 6901</a>.
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* fails, an appropriate exception is thrown. If the navigation fails to find
|
||||
* a match, a JSONPointerException is thrown.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2016-05-14
|
||||
*/
|
||||
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<String> refTokens = new ArrayList<String>();
|
||||
|
||||
/**
|
||||
* 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:
|
||||
*
|
||||
* <pre><code>
|
||||
* JSONPointer pointer = JSONPointer.builder()
|
||||
* .append("obj")
|
||||
* .append("other~key").append("another/key")
|
||||
* .append("\"")
|
||||
* .append(0)
|
||||
* .build();
|
||||
* </code></pre>
|
||||
*
|
||||
* @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<String> refTokens;
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public JSONPointer(final String pointer) {
|
||||
if (pointer == null) {
|
||||
throw new NullPointerException("pointer cannot be null");
|
||||
}
|
||||
if (pointer.isEmpty() || pointer.equals("#")) {
|
||||
this.refTokens = Collections.emptyList();
|
||||
return;
|
||||
}
|
||||
String refs;
|
||||
if (pointer.startsWith("#/")) {
|
||||
refs = pointer.substring(2);
|
||||
try {
|
||||
refs = URLDecoder.decode(refs, ENCODING);
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else if (pointer.startsWith("/")) {
|
||||
refs = pointer.substring(1);
|
||||
} else {
|
||||
throw new IllegalArgumentException("a JSON pointer should start with '/' or '#/'");
|
||||
}
|
||||
this.refTokens = new ArrayList<String>();
|
||||
int slashIdx = -1;
|
||||
int prevSlashIdx = 0;
|
||||
do {
|
||||
prevSlashIdx = slashIdx + 1;
|
||||
slashIdx = refs.indexOf('/', prevSlashIdx);
|
||||
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("");
|
||||
} else if (slashIdx >= 0) {
|
||||
final String token = refs.substring(prevSlashIdx, slashIdx);
|
||||
this.refTokens.add(unescape(token));
|
||||
} else {
|
||||
// last item after separator, or no separator at all.
|
||||
final String token = refs.substring(prevSlashIdx);
|
||||
this.refTokens.add(unescape(token));
|
||||
}
|
||||
} while (slashIdx >= 0);
|
||||
// using split does not take into account consecutive separators or "ending nulls"
|
||||
//for (String token : refs.split("/")) {
|
||||
// this.refTokens.add(unescape(token));
|
||||
//}
|
||||
}
|
||||
|
||||
public JSONPointer(List<String> refTokens) {
|
||||
this.refTokens = new ArrayList<String>(refTokens);
|
||||
}
|
||||
|
||||
private static String unescape(String token) {
|
||||
return token.replace("~1", "/").replace("~0", "~")
|
||||
.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.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public Object queryFrom(Object document) throws JSONPointerException {
|
||||
if (this.refTokens.isEmpty()) {
|
||||
return document;
|
||||
}
|
||||
Object current = document;
|
||||
for (String token : this.refTokens) {
|
||||
if (current instanceof JSONObject) {
|
||||
current = ((JSONObject) current).opt(unescape(token));
|
||||
} else if (current instanceof JSONArray) {
|
||||
current = readByIndexToken(current, token);
|
||||
} else {
|
||||
throw new JSONPointerException(format(
|
||||
"value [%s] is not an array or object therefore its key %s cannot be resolved", current,
|
||||
token));
|
||||
}
|
||||
}
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder rval = new StringBuilder("");
|
||||
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
|
||||
*/
|
||||
public String toURIFragment() {
|
||||
try {
|
||||
StringBuilder rval = new StringBuilder("#");
|
||||
for (String token : this.refTokens) {
|
||||
rval.append('/').append(URLEncoder.encode(token, ENCODING));
|
||||
}
|
||||
return rval.toString();
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
45
src/main/java/jsonrank/JSONPointerException.java
Normal file
45
src/main/java/jsonrank/JSONPointerException.java
Normal file
|
@ -0,0 +1,45 @@
|
|||
package jsonrank;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* The JSONPointerException is thrown by {@link JSONPointer} if an error occurs
|
||||
* during evaluating a pointer.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2016-05-13
|
||||
*/
|
||||
public class JSONPointerException extends JSONException {
|
||||
private static final long serialVersionUID = 8872944667561856751L;
|
||||
|
||||
public JSONPointerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONPointerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
43
src/main/java/jsonrank/JSONPropertyIgnore.java
Normal file
43
src/main/java/jsonrank/JSONPropertyIgnore.java
Normal file
|
@ -0,0 +1,43 @@
|
|||
package jsonrank;
|
||||
|
||||
/*
|
||||
Copyright (c) 2018 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({METHOD})
|
||||
/**
|
||||
* Use this annotation on a getter method to override the Bean name
|
||||
* parser for Bean -> JSONObject mapping. If this annotation is
|
||||
* present at any level in the class hierarchy, then the method will
|
||||
* not be serialized from the bean into the JSONObject.
|
||||
*/
|
||||
public @interface JSONPropertyIgnore { }
|
47
src/main/java/jsonrank/JSONPropertyName.java
Normal file
47
src/main/java/jsonrank/JSONPropertyName.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package jsonrank;
|
||||
|
||||
/*
|
||||
Copyright (c) 2018 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import static java.lang.annotation.ElementType.METHOD;
|
||||
import static java.lang.annotation.RetentionPolicy.RUNTIME;
|
||||
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({METHOD})
|
||||
/**
|
||||
* Use this annotation on a getter method to override the Bean name
|
||||
* parser for Bean -> JSONObject mapping. A value set to empty string <code>""</code>
|
||||
* will have the Bean parser fall back to the default field name processing.
|
||||
*/
|
||||
public @interface JSONPropertyName {
|
||||
/**
|
||||
* @return The name of the property as to be used in the JSON Object.
|
||||
*/
|
||||
String value();
|
||||
}
|
18
src/main/java/jsonrank/JSONString.java
Normal file
18
src/main/java/jsonrank/JSONString.java
Normal file
|
@ -0,0 +1,18 @@
|
|||
package jsonrank;
|
||||
/**
|
||||
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
|
||||
* method so that a class can change the behavior of
|
||||
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>,
|
||||
* and <code>JSONWriter.value(</code>Object<code>)</code>. The
|
||||
* <code>toJSONString</code> method will be used instead of the default behavior
|
||||
* of using the Object's <code>toString()</code> method and quoting the result.
|
||||
*/
|
||||
public interface JSONString {
|
||||
/**
|
||||
* The <code>toJSONString</code> method allows a class to produce its own JSON
|
||||
* serialization.
|
||||
*
|
||||
* @return A strictly syntactically correct JSON text.
|
||||
*/
|
||||
public String toJSONString();
|
||||
}
|
79
src/main/java/jsonrank/JSONStringer.java
Normal file
79
src/main/java/jsonrank/JSONStringer.java
Normal file
|
@ -0,0 +1,79 @@
|
|||
package jsonrank;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import java.io.StringWriter;
|
||||
|
||||
/**
|
||||
* JSONStringer provides a quick and convenient way of producing JSON text.
|
||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
||||
* added, so the results are ready for transmission or storage. Each instance of
|
||||
* JSONStringer can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONStringer instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting cascade style. For example, <pre>
|
||||
* myString = new JSONStringer()
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject()
|
||||
* .toString();</pre> which produces the string <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONStringer adds them for
|
||||
* you. Objects and arrays can be nested up to 20 levels deep.
|
||||
* <p>
|
||||
* This can sometimes be easier than using a JSONObject to build a string.
|
||||
* @author JSON.org
|
||||
* @version 2015-12-09
|
||||
*/
|
||||
public class JSONStringer extends JSONWriter {
|
||||
/**
|
||||
* Make a fresh JSONStringer. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONStringer() {
|
||||
super(new StringWriter());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JSON text. This method is used to obtain the product of the
|
||||
* JSONStringer instance. It will return <code>null</code> if there was a
|
||||
* problem in the construction of the JSON text (such as the calls to
|
||||
* <code>array</code> were not properly balanced with calls to
|
||||
* <code>endArray</code>).
|
||||
* @return The JSON text.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
526
src/main/java/jsonrank/JSONTokener.java
Normal file
526
src/main/java/jsonrank/JSONTokener.java
Normal file
|
@ -0,0 +1,526 @@
|
|||
package jsonrank;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
/*
|
||||
Copyright (c) 2002 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
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. */
|
||||
private final Reader reader;
|
||||
/** flag to indicate that a previous character was requested. */
|
||||
private boolean usePrevious;
|
||||
/** 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.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported()
|
||||
? reader
|
||||
: new BufferedReader(reader);
|
||||
this.eof = false;
|
||||
this.usePrevious = false;
|
||||
this.previous = 0;
|
||||
this.index = 0;
|
||||
this.character = 1;
|
||||
this.characterPreviousLine = 0;
|
||||
this.line = 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from an InputStream. The caller must close the input stream.
|
||||
* @param inputStream The source.
|
||||
*/
|
||||
public JSONTokener(InputStream inputStream) {
|
||||
this(new InputStreamReader(inputStream));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a 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.
|
||||
*/
|
||||
public static int dehexchar(char c) {
|
||||
if (c >= '0' && c <= '9') {
|
||||
return c - '0';
|
||||
}
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
return c - ('A' - 10);
|
||||
}
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
return c - ('a' - 10);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
return this.eof && !this.usePrevious;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
if(this.usePrevious) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
this.reader.mark(1);
|
||||
} catch (IOException e) {
|
||||
throw new JSONException("Unable to preserve stream position", e);
|
||||
}
|
||||
try {
|
||||
// -1 is EOF, but next() can not consume the null character '\0'
|
||||
if(this.reader.read() <= 0) {
|
||||
this.eof = true;
|
||||
return false;
|
||||
}
|
||||
this.reader.reset();
|
||||
} catch (IOException e) {
|
||||
throw new JSONException("Unable to read the next character from the stream", e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
* @throws JSONException Thrown if there is an error reading the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
int c;
|
||||
if (this.usePrevious) {
|
||||
this.usePrevious = false;
|
||||
c = this.previous;
|
||||
} else {
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
}
|
||||
if (c <= 0) { // End of stream
|
||||
this.eof = true;
|
||||
return 0;
|
||||
}
|
||||
this.incrementIndexes(c);
|
||||
this.previous = (char) c;
|
||||
return this.previous;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
this.index++;
|
||||
if(c=='\r') {
|
||||
this.line++;
|
||||
this.characterPreviousLine = this.character;
|
||||
this.character=0;
|
||||
}else if (c=='\n') {
|
||||
if(this.previous != '\r') {
|
||||
this.line++;
|
||||
this.characterPreviousLine = this.character;
|
||||
}
|
||||
this.character=0;
|
||||
} else {
|
||||
this.character++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public char next(char c) throws JSONException {
|
||||
char n = this.next();
|
||||
if (n != c) {
|
||||
if(n > 0) {
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw '" +
|
||||
n + "'");
|
||||
}
|
||||
throw this.syntaxError("Expected '" + c + "' and instead saw ''");
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public String next(int n) throws JSONException {
|
||||
if (n == 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
char[] chars = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
while (pos < n) {
|
||||
chars[pos] = this.next();
|
||||
if (this.end()) {
|
||||
throw this.syntaxError("Substring bounds error");
|
||||
}
|
||||
pos += 1;
|
||||
}
|
||||
return new String(chars);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
* @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 (;;) {
|
||||
char c = this.next();
|
||||
if (c == 0 || c > ' ') {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the characters up to the next close quote character.
|
||||
* 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
|
||||
* <code>"</code> <small>(double quote)</small> or
|
||||
* <code>'</code> <small>(single quote)</small>.
|
||||
* @return A String.
|
||||
* @throws JSONException Unterminated string.
|
||||
*/
|
||||
public String nextString(char quote) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @throws JSONException Thrown if there is an error while searching
|
||||
* for the delimiter
|
||||
*/
|
||||
public String nextTo(char delimiter) throws JSONException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
char c = this.next();
|
||||
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (;;) {
|
||||
c = this.next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 ||
|
||||
c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
this.back();
|
||||
}
|
||||
return sb.toString().trim();
|
||||
}
|
||||
sb.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle unquoted text. This could be the values true, false, or
|
||||
* null, or it can be a number. An implementation (such as this one)
|
||||
* is allowed to also accept non-standard forms.
|
||||
*
|
||||
* Accumulate characters until we reach the end of the text or a
|
||||
* formatting character.
|
||||
*/
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = this.next();
|
||||
}
|
||||
if (!this.eof) {
|
||||
this.back();
|
||||
}
|
||||
|
||||
string = sb.toString().trim();
|
||||
if ("".equals(string)) {
|
||||
throw this.syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(string);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
long startIndex = this.index;
|
||||
long startCharacter = this.character;
|
||||
long startLine = this.line;
|
||||
this.reader.mark(1000000);
|
||||
do {
|
||||
c = this.next();
|
||||
if (c == 0) {
|
||||
// in some readers, reset() may throw an exception if
|
||||
// the remaining portion of the input is greater than
|
||||
// the mark size (1,000,000 above).
|
||||
this.reader.reset();
|
||||
this.index = startIndex;
|
||||
this.character = startCharacter;
|
||||
this.line = startLine;
|
||||
return 0;
|
||||
}
|
||||
} while (c != to);
|
||||
this.reader.mark(1);
|
||||
} catch (IOException exception) {
|
||||
throw new JSONException(exception);
|
||||
}
|
||||
this.back();
|
||||
return c;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message) {
|
||||
return new JSONException(message + this.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSONException to signal a syntax error.
|
||||
*
|
||||
* @param message The error message.
|
||||
* @param causedBy The throwable that caused the error.
|
||||
* @return A JSONException object, suitable for throwing
|
||||
*/
|
||||
public JSONException syntaxError(String message, Throwable causedBy) {
|
||||
return new JSONException(message + this.toString(), causedBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at {index} [character {character} line {line}]"
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return " at " + this.index + " [character " + this.character + " line " +
|
||||
this.line + "]";
|
||||
}
|
||||
}
|
413
src/main/java/jsonrank/JSONWriter.java
Normal file
413
src/main/java/jsonrank/JSONWriter.java
Normal file
|
@ -0,0 +1,413 @@
|
|||
package jsonrank;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
/*
|
||||
Copyright (c) 2006 JSON.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
The Software shall be used for Good, not Evil.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* JSONWriter provides a quick and convenient way of producing JSON text.
|
||||
* The texts produced strictly conform to JSON syntax rules. No whitespace is
|
||||
* added, so the results are ready for transmission or storage. Each instance of
|
||||
* JSONWriter can produce one JSON text.
|
||||
* <p>
|
||||
* A JSONWriter instance provides a <code>value</code> method for appending
|
||||
* values to the
|
||||
* text, and a <code>key</code>
|
||||
* method for adding keys before values in objects. There are <code>array</code>
|
||||
* and <code>endArray</code> methods that make and bound array values, and
|
||||
* <code>object</code> and <code>endObject</code> methods which make and bound
|
||||
* object values. All of these methods return the JSONWriter instance,
|
||||
* permitting a cascade style. For example, <pre>
|
||||
* new JSONWriter(myWriter)
|
||||
* .object()
|
||||
* .key("JSON")
|
||||
* .value("Hello, World!")
|
||||
* .endObject();</pre> which writes <pre>
|
||||
* {"JSON":"Hello, World!"}</pre>
|
||||
* <p>
|
||||
* The first method called must be <code>array</code> or <code>object</code>.
|
||||
* There are no methods for adding commas or colons. JSONWriter adds them for
|
||||
* you. Objects and arrays can be nested up to 200 levels deep.
|
||||
* <p>
|
||||
* 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.
|
||||
*/
|
||||
private boolean comma;
|
||||
|
||||
/**
|
||||
* The current mode. Values:
|
||||
* 'a' (array),
|
||||
* 'd' (done),
|
||||
* 'i' (initial),
|
||||
* 'k' (key),
|
||||
* '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;
|
||||
|
||||
/**
|
||||
* Make a fresh JSONWriter. It can be used to build one JSON text.
|
||||
*/
|
||||
public JSONWriter(Appendable w) {
|
||||
this.comma = false;
|
||||
this.mode = 'i';
|
||||
this.stack = new JSONObject[maxdepth];
|
||||
this.top = 0;
|
||||
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
|
||||
* <code>endArray</code> will be appended to this array. The
|
||||
* <code>endArray</code> 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
|
||||
* <code>array</code>.
|
||||
* @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
|
||||
* <code>object</code>.
|
||||
* @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
|
||||
* <code>endObject</code> will be appended to this object. The
|
||||
* <code>endObject</code> 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
|
||||
* JSON text. The method is required to produce a strictly conforming text.
|
||||
* If the object does not contain a toJSONString method (which is the most
|
||||
* common case), then a text will be produced by other means. If the value
|
||||
* is an array or Collection, then a JSONArray will be made from it and its
|
||||
* toJSONString method will be called. If the value is a MAP, then a
|
||||
* JSONObject will be made from it and its toJSONString method will be
|
||||
* called. Otherwise, the value's toString method will be called, and the
|
||||
* result will be quoted.
|
||||
*
|
||||
* <p>
|
||||
* Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @param value
|
||||
* The value to be serialized.
|
||||
* @return a printable, displayable, transmittable representation of the
|
||||
* object, beginning with <code>{</code> <small>(left
|
||||
* brace)</small> and ending with <code>}</code> <small>(right
|
||||
* brace)</small>.
|
||||
* @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)) {
|
||||
return "null";
|
||||
}
|
||||
if (value instanceof JSONString) {
|
||||
String object;
|
||||
try {
|
||||
object = ((JSONString) value).toJSONString();
|
||||
} catch (Exception e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
if (object != null) {
|
||||
return object;
|
||||
}
|
||||
throw new JSONException("Bad value from toJSONString: " + object);
|
||||
}
|
||||
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()) {
|
||||
// Close enough to a JSON number that we will return it unquoted
|
||||
return numberAsString;
|
||||
}
|
||||
// The Number value is not a valid JSON number.
|
||||
// Instead we will quote it as a string
|
||||
return JSONObject.quote(numberAsString);
|
||||
}
|
||||
if (value instanceof Boolean || value instanceof JSONObject
|
||||
|| value instanceof JSONArray) {
|
||||
return value.toString();
|
||||
}
|
||||
if (value instanceof Map) {
|
||||
Map<?, ?> map = (Map<?, ?>) value;
|
||||
return new JSONObject(map).toString();
|
||||
}
|
||||
if (value instanceof Collection) {
|
||||
Collection<?> coll = (Collection<?>) value;
|
||||
return new JSONArray(coll).toString();
|
||||
}
|
||||
if (value.getClass().isArray()) {
|
||||
return new JSONArray(value).toString();
|
||||
}
|
||||
if(value instanceof Enum<?>){
|
||||
return JSONObject.quote(((Enum<?>)value).name());
|
||||
}
|
||||
return JSONObject.quote(value.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append either the value <code>true</code> or the value
|
||||
* <code>false</code>.
|
||||
* @param b A boolean.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONWriter value(boolean b) throws JSONException {
|
||||
return this.append(b ? "true" : "false");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value.
|
||||
* @param d A double.
|
||||
* @return this
|
||||
* @throws JSONException If the number is not finite.
|
||||
*/
|
||||
public JSONWriter value(double d) throws JSONException {
|
||||
return this.value(Double.valueOf(d));
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a long value.
|
||||
* @param l A long.
|
||||
* @return this
|
||||
* @throws JSONException
|
||||
*/
|
||||
public JSONWriter value(long l) throws JSONException {
|
||||
return this.append(Long.toString(l));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return this
|
||||
* @throws JSONException If the value is out of sequence.
|
||||
*/
|
||||
public JSONWriter value(Object object) throws JSONException {
|
||||
return this.append(valueToString(object));
|
||||
}
|
||||
}
|
12
src/main/resources/config.yml
Normal file
12
src/main/resources/config.yml
Normal file
|
@ -0,0 +1,12 @@
|
|||
时间:
|
||||
观众名:
|
||||
礼物积分: 200
|
||||
友好积分: 100
|
||||
恶人积分: 100
|
||||
# 例如
|
||||
# 2024-09-19:
|
||||
# 杨总光临:
|
||||
# 礼物积分: 200
|
||||
# 友好积分: 100
|
||||
# 恶人积分: 100
|
||||
#
|
6
src/main/resources/plugin.yml
Normal file
6
src/main/resources/plugin.yml
Normal file
|
@ -0,0 +1,6 @@
|
|||
name: GameRanking
|
||||
main: com.yaohun.gameranking.GameRanking
|
||||
version: 1.0
|
||||
api-version: '1.18'
|
||||
commands:
|
||||
gamerank:
|
1
src/main/resources/user.yml
Normal file
1
src/main/resources/user.yml
Normal file
|
@ -0,0 +1 @@
|
|||
观众名:
|
Loading…
Reference in New Issue
Block a user