mirror of
https://bitbucket.org/projectswg/launcherupdater.git
synced 2026-01-15 21:04:24 -05:00
Added the game launcher JAR to downloaded files, remote ssh deploy, and removed old copy-paste of a JSON library
This commit is contained in:
18
.gitignore
vendored
18
.gitignore
vendored
@@ -1,5 +1,13 @@
|
||||
/bin/
|
||||
/build/
|
||||
/.gradle/
|
||||
/.settings/
|
||||
/update/
|
||||
.settings/
|
||||
|
||||
# IDE-based
|
||||
*.iml
|
||||
out/
|
||||
.idea/
|
||||
|
||||
# Gradle
|
||||
bin/
|
||||
build/
|
||||
.gradle/
|
||||
gradle
|
||||
gradlew*
|
||||
|
||||
Submodule PSWGCommon updated: 8505a996c3...bec3a8e1e5
Submodule PSWGCommonFX updated: 32a80807c2...a12c894c87
57
build.gradle
57
build.gradle
@@ -1,6 +1,19 @@
|
||||
apply plugin: 'application'
|
||||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply plugin: 'java'
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.0'
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'com.github.johnrengelman.shadow' version '2.0.2'
|
||||
id 'org.hidetake.ssh' version '2.9.0'
|
||||
id 'application'
|
||||
id 'java'
|
||||
id 'idea'
|
||||
}
|
||||
|
||||
mainClassName = 'com.projectswg.installer.LauncherUpdater'
|
||||
|
||||
@@ -11,26 +24,46 @@ manifest {
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs = ['src']
|
||||
includes ['**/*.java']
|
||||
srcDir 'src'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shadowJar {
|
||||
archiveName = "ProjectSWG.jar"
|
||||
baseName = "ProjectSWG"
|
||||
classifier = null
|
||||
version = null
|
||||
}
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':PSWGCommon')
|
||||
compile project(':PSWGCommonFX')
|
||||
|
||||
compile group: "me.joshlarson", name: "fast-json", version: "2.2.3"
|
||||
}
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://plugins.gradle.org/m2/' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.github.jengelman.gradle.plugins:shadow:2.0.0'
|
||||
task wrapper(type: Wrapper) {
|
||||
gradleVersion = "4.4"
|
||||
}
|
||||
|
||||
remotes {
|
||||
server {
|
||||
host = 'patch1.projectswg.com'
|
||||
user = 'root'
|
||||
identity = file("${System.properties['user.home']}/.ssh/id_rsa_pswg_patch")
|
||||
}
|
||||
}
|
||||
|
||||
task deploy(dependsOn: shadowJar) {
|
||||
doLast {
|
||||
ssh.run {
|
||||
session(remotes.server) {
|
||||
put from: shadowJar.archivePath, into: '/var/www/patch1.projectswg.com/launcher_patch/ProjectSWG.jar'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
rootProject.name = 'LauncherUpdater'
|
||||
include ':PSWGCommon', ':PSWGCommonFX'
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
*/
|
||||
package com.projectswg.installer;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import com.projectswg.common.concurrency.Delay;
|
||||
import com.projectswg.common.control.IntentManager;
|
||||
import com.projectswg.common.debug.Log;
|
||||
@@ -31,14 +27,16 @@ import com.projectswg.common.javafx.ResourceUtilities;
|
||||
import com.projectswg.common.process.JarProcessBuilder;
|
||||
import com.projectswg.common.process.JarProcessBuilder.MemoryUnit;
|
||||
import com.projectswg.common.utilities.LocalUtilities;
|
||||
|
||||
import javafx.application.Platform;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
public class LauncherUpdater {
|
||||
|
||||
private static final String LOCAL_JAR_FILE = "Launcher.jar";
|
||||
|
||||
public static void main(String [] args) throws IOException {
|
||||
public static void main(String [] args) {
|
||||
LocalUtilities.setApplicationName(".projectswg/launcher");
|
||||
ResourceUtilities.setPrimarySource(LauncherUpdater.class);
|
||||
Log.addWrapper(new ConsoleLogWrapper(LogLevel.VERBOSE));
|
||||
@@ -77,10 +75,9 @@ public class LauncherUpdater {
|
||||
|
||||
private static boolean launch() {
|
||||
try {
|
||||
File updaterDirectory = LocalUtilities.getSubApplicationDirectory("updater");
|
||||
File java = new File(System.getProperty("java.home"), "bin/java");
|
||||
File updaterDirectory = LocalUtilities.getApplicationDirectory();
|
||||
File src = new File(updaterDirectory, LOCAL_JAR_FILE);
|
||||
JarProcessBuilder builder = new JarProcessBuilder(java, src).setMemory(256, 256, MemoryUnit.MEGABYTES).inheritIO();
|
||||
JarProcessBuilder builder = new JarProcessBuilder(JarProcessBuilder.getJavaPath(), src).setMemory(256, 256, MemoryUnit.MEGABYTES).inheritIO();
|
||||
Log.i("Launching jar... '%s'", src);
|
||||
Platform.runLater(LauncherUpdateGUI.getInstance()::close);
|
||||
builder.start().waitFor();
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
*/
|
||||
package com.projectswg.installer;
|
||||
|
||||
import com.projectswg.common.concurrency.Delay;
|
||||
import com.projectswg.common.debug.Assert;
|
||||
import com.projectswg.common.debug.Log;
|
||||
import com.projectswg.common.javafx.ResourceUtilities;
|
||||
import com.projectswg.common.utilities.LocalUtilities;
|
||||
import me.joshlarson.json.JSON;
|
||||
import me.joshlarson.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -27,19 +35,12 @@ import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
import com.projectswg.common.concurrency.Delay;
|
||||
import com.projectswg.common.debug.Assert;
|
||||
import com.projectswg.common.debug.Log;
|
||||
import com.projectswg.common.javafx.ResourceUtilities;
|
||||
import com.projectswg.common.utilities.LocalUtilities;
|
||||
import com.projectswg.installer.json.JSON;
|
||||
import com.projectswg.installer.json.JSONObject;
|
||||
|
||||
public class RemoteInstaller {
|
||||
|
||||
private static final String MANIFEST = "manifest.json";
|
||||
private static final String INSTALLER = "ProjectSWG.jar";
|
||||
private static final String LAUNCHER = "Launcher.jar";
|
||||
private static final String LAUNCHER_GAME = "GameLauncher.jar";
|
||||
private static final String DATA = "data.zip";
|
||||
|
||||
private static final String REMOTE_URL = "patch1.projectswg.com";
|
||||
@@ -47,6 +48,7 @@ public class RemoteInstaller {
|
||||
private static final String REMOTE_MANIFEST = REMOTE_PATH+'/'+MANIFEST;
|
||||
private static final String REMOTE_INSTALLER = REMOTE_PATH+'/'+INSTALLER;
|
||||
private static final String REMOTE_LAUNCHER = REMOTE_PATH+'/'+LAUNCHER;
|
||||
private static final String REMOTE_LAUNCHER_GAME= REMOTE_PATH+'/'+LAUNCHER_GAME;
|
||||
private static final String REMOTE_DATA = REMOTE_PATH+'/'+DATA;
|
||||
|
||||
private final InstallStatusCallback callback;
|
||||
@@ -59,7 +61,7 @@ public class RemoteInstaller {
|
||||
|
||||
private RemoteInstaller(InstallStatusCallback callback) {
|
||||
this.callback = callback;
|
||||
this.updateDirectory = LocalUtilities.getSubApplicationDirectory("updater");
|
||||
this.updateDirectory = LocalUtilities.getApplicationDirectory();
|
||||
this.status = new AtomicReference<>(InstallStatus.CREATED);
|
||||
this.installerVersion = new AtomicReference<>(null);
|
||||
this.launcherVersion = new AtomicReference<>(null);
|
||||
@@ -92,7 +94,7 @@ public class RemoteInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean testConnection() throws InstallerException {
|
||||
private boolean testConnection() {
|
||||
verifyStatus(InstallStatus.INITIALIZED, InstallStatus.CONNECTED);
|
||||
try {
|
||||
RemoteUtilities.testConnection(REMOTE_URL, REMOTE_MANIFEST);
|
||||
@@ -153,10 +155,15 @@ public class RemoteInstaller {
|
||||
private void downloadLauncherUpdates() throws InstallerException {
|
||||
updateStatus(.2, "Starting launcher download...");
|
||||
try {
|
||||
double basePercent = 0.2;
|
||||
double totalPercent = ((int) (0.8 / 3 * 100)) / 100.0;
|
||||
Log.d(" Downloading launcher...");
|
||||
RemoteUtilities.download(REMOTE_URL, REMOTE_LAUNCHER, new File(updateDirectory, LAUNCHER), (download, percent) -> updateStatus(.2+percent*0.35, "Downloading launcher patch..."));
|
||||
download(REMOTE_LAUNCHER, new File(updateDirectory, LAUNCHER), basePercent, totalPercent, "Downloading launcher patch [core]...");
|
||||
basePercent += totalPercent;
|
||||
download(REMOTE_LAUNCHER_GAME, new File(updateDirectory, LAUNCHER_GAME), basePercent, totalPercent, "Downloading launcher patch [game]...");
|
||||
basePercent += totalPercent;
|
||||
Log.d(" Downloading data...");
|
||||
RemoteUtilities.download(REMOTE_URL, REMOTE_DATA, new File(updateDirectory, DATA), (download, percent) -> updateStatus(.55+percent*0.35, "Downloading data patch..."));
|
||||
download(REMOTE_DATA, new File(updateDirectory, DATA), basePercent, totalPercent, "Downloading data patch...");
|
||||
} catch (IOException e) {
|
||||
Log.e(e);
|
||||
throw new InstallerException("downloading installer updates", e);
|
||||
@@ -212,7 +219,7 @@ public class RemoteInstaller {
|
||||
}
|
||||
}
|
||||
|
||||
private void completeInstallerDownload(AtomicBoolean running) throws InstallerException {
|
||||
private void completeInstallerDownload(AtomicBoolean running) {
|
||||
Log.d(" Terminal operation. Waiting for user to restart launcher.");
|
||||
updateStatus(1, "Please restart the launcher to complete the update.");
|
||||
while (running.get()) { // Wait for user to restart the launcher
|
||||
@@ -229,6 +236,10 @@ public class RemoteInstaller {
|
||||
Assert.test(status.compareAndSet(oldStatus, newStatus), "Unable to update status! Expected: " + oldStatus);
|
||||
}
|
||||
|
||||
private void download(String remotePath, File local, double basePercent, double totalPercent, String status) throws IOException {
|
||||
RemoteUtilities.download(REMOTE_URL, remotePath, local, (download, percent) -> updateStatus(basePercent+percent*totalPercent, status));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safe function to reinstall the application. All failures are handled
|
||||
* @param callback the callback for progress updates
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Provides convenience methods for stream operations that do automatic resource cleanup
|
||||
*/
|
||||
public class JSON {
|
||||
|
||||
/**
|
||||
* Opens a new JSONInputStream with the specified InputStream and reads a JSONObject. After
|
||||
* reading, the input streams are closed
|
||||
*
|
||||
* @param is the input stream to read from
|
||||
* @param printError TRUE if exception stack traces should be printed, FALSE otherwise
|
||||
* @return the JSONObject read from the stream, or null if there was an exception
|
||||
*/
|
||||
public static JSONObject readObject(InputStream is, boolean printError) {
|
||||
JSONInputStream in = new JSONInputStream(is);
|
||||
try {
|
||||
return in.readObject();
|
||||
} catch (IOException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
} catch (JSONException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new JSONInputStream with the specified string and reads a JSONObject
|
||||
*
|
||||
* @param str the string to read from
|
||||
* @param printError TRUE if exception stack traces should be printed, FALSE otherwise
|
||||
* @return the JSONObject read from the string, or null if there was an exception
|
||||
*/
|
||||
public static JSONObject readObject(String str, boolean printError) {
|
||||
return readObject(new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)), printError);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new JSONInputStream with the specified InputStream and reads a JSONArray. After
|
||||
* reading, the input streams are closed
|
||||
*
|
||||
* @param is the input stream to read from
|
||||
* @param printError TRUE if exception stack traces should be printed, FALSE otherwise
|
||||
* @return the JSONArray read from the stream, or null if there was an exception
|
||||
*/
|
||||
public static JSONArray readArray(InputStream is, boolean printError) {
|
||||
JSONInputStream in = new JSONInputStream(is);
|
||||
try {
|
||||
return in.readArray();
|
||||
} catch (IOException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
} catch (JSONException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
in.close();
|
||||
} catch (IOException e) {
|
||||
if (printError)
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new JSONInputStream with the specified string and reads a JSONArray
|
||||
*
|
||||
* @param str the string to read from
|
||||
* @param printError TRUE if exception stack traces should be printed, FALSE otherwise
|
||||
* @return the JSONArray read from the string, or null if there was an exception
|
||||
*/
|
||||
public static JSONArray readArray(String str, boolean printError) {
|
||||
return readArray(new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)), printError);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
/**
|
||||
* This class contains a list of values, which can be one of the following types: JSONObject,
|
||||
* JSONArray, Number, Boolean, String, or null
|
||||
*/
|
||||
public class JSONArray implements List<Object>, Iterable<Object> {
|
||||
|
||||
private final ArrayList<Object> array; // Specifically ArrayList for null value support
|
||||
|
||||
public JSONArray() {
|
||||
array = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return array.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
array.clear();
|
||||
}
|
||||
|
||||
public Object remove(int index) {
|
||||
if (index < 0 || index >= array.size())
|
||||
throw new IndexOutOfBoundsException("Specified index " + index + " is out of range! [0, " + size() + ")");
|
||||
return array.remove(index);
|
||||
}
|
||||
|
||||
public void add(int index, Object o) {
|
||||
if (o instanceof JSONObject || o instanceof JSONArray)
|
||||
array.add(index, o);
|
||||
else if (o instanceof Number || o instanceof Boolean)
|
||||
array.add(index, o);
|
||||
else if (o instanceof String)
|
||||
array.add(index, o);
|
||||
else if (o == null)
|
||||
array.add(index, null);
|
||||
else
|
||||
throw new IllegalArgumentException("Object must be of type: JSONObject, JSONArray, Number, Boolean, String, or null!");
|
||||
}
|
||||
|
||||
public boolean add(Object o) {
|
||||
if (o instanceof JSONObject)
|
||||
add((JSONObject) o);
|
||||
else if (o instanceof JSONArray)
|
||||
add((JSONArray) o);
|
||||
else if (o instanceof Number)
|
||||
add((Number) o);
|
||||
else if (o instanceof Boolean)
|
||||
add((Boolean) o);
|
||||
else if (o instanceof String)
|
||||
add((String) o);
|
||||
else if (o == null)
|
||||
addNull();
|
||||
else
|
||||
throw new IllegalArgumentException("Object must be of type: JSONObject, JSONArray, Number, Boolean, String, or null!");
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addAll(Collection<? extends Object> c) {
|
||||
ensureCapacity(size() + c.size());
|
||||
for (Object o : c) {
|
||||
add(o);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean addAll(int index, Collection<? extends Object> c) {
|
||||
ensureCapacity(size() + c.size());
|
||||
int i = index;
|
||||
for (Object o : c) {
|
||||
add(i++, o);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean containsAll(Collection<?> c) {
|
||||
return array.containsAll(c);
|
||||
}
|
||||
|
||||
public void ensureCapacity(int minCapacity) {
|
||||
array.ensureCapacity(minCapacity);
|
||||
}
|
||||
|
||||
public int indexOf(Object o) {
|
||||
return array.indexOf(o);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return array.isEmpty();
|
||||
}
|
||||
|
||||
public int lastIndexOf(Object o) {
|
||||
return array.lastIndexOf(o);
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
return array.remove(o);
|
||||
}
|
||||
|
||||
public boolean removeAll(Collection<?> c) {
|
||||
return array.removeAll(c);
|
||||
}
|
||||
|
||||
public boolean retainAll(Collection<?> c) {
|
||||
return array.retainAll(c);
|
||||
}
|
||||
|
||||
public Object set(int index, Object element) {
|
||||
return array.set(index, element);
|
||||
}
|
||||
|
||||
public List<Object> subList(int fromIndex, int toIndex) {
|
||||
return array.subList(fromIndex, toIndex);
|
||||
}
|
||||
|
||||
public Object[] toArray() {
|
||||
return array.toArray();
|
||||
}
|
||||
|
||||
public <T> T[] toArray(T[] a) {
|
||||
return array.toArray(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Object> listIterator() {
|
||||
return array.listIterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListIterator<Object> listIterator(int index) {
|
||||
return array.listIterator(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JSONObject to the array
|
||||
*
|
||||
* @param obj the JSONObject to add
|
||||
*/
|
||||
public void add(JSONObject obj) {
|
||||
array.add(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a JSONArray to the array
|
||||
*
|
||||
* @param array the JSONArray to add
|
||||
*/
|
||||
public void add(JSONArray array) {
|
||||
this.array.add(array);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a number to the array
|
||||
*
|
||||
* @param n the number to add
|
||||
*/
|
||||
public void add(Number n) {
|
||||
array.add(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a boolean to the array
|
||||
*
|
||||
* @param b the boolean to add
|
||||
*/
|
||||
public void add(Boolean b) {
|
||||
array.add(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a string to the array
|
||||
*
|
||||
* @param str the string to add
|
||||
*/
|
||||
public void add(String str) {
|
||||
array.add(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a null value to the array
|
||||
*/
|
||||
public void addNull() {
|
||||
array.add(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object will always be one
|
||||
* of the supported JSON types: JSONObject, JSONArray, Number, Boolean, String, or null
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the object at the specified index
|
||||
*/
|
||||
public Object get(int index) {
|
||||
return array.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* JSONObject
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the object at the specified index
|
||||
* @throws ClassCastException if the object is not a JSONObject
|
||||
*/
|
||||
public JSONObject getObject(int index) {
|
||||
return (JSONObject) get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* JSONArray
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the array at the specified index
|
||||
* @throws ClassCastException if the object is not a JSONArray
|
||||
*/
|
||||
public JSONArray getArray(int index) {
|
||||
return (JSONArray) get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a int
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the int at the specified index
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public int getInt(int index) {
|
||||
return ((Number) get(index)).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* long
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the long at the specified index
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public long getLong(int index) {
|
||||
return ((Number) get(index)).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* float
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the float at the specified index
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public float getFloat(int index) {
|
||||
return ((Number) get(index)).floatValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* double
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the double at the specified index
|
||||
* @throws NullPointerException if the object is null
|
||||
*/
|
||||
public double getDouble(int index) {
|
||||
return ((Number) get(index)).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* boolean
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the boolean at the specified index
|
||||
* @throws NullPointerException if the object is null
|
||||
* @throws ClassCastException if the object is not a boolean
|
||||
*/
|
||||
public boolean getBoolean(int index) {
|
||||
return (boolean) get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the object at the specified index from the array. The returned object is casted to a
|
||||
* String
|
||||
*
|
||||
* @param index the index to retrieve over the interval [0, size())
|
||||
* @return the string at the specified index
|
||||
* @throws ClassCastException if the object is not a String
|
||||
*/
|
||||
public String getString(int index) {
|
||||
return (String) get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not the specified object exists inside this array
|
||||
*
|
||||
* @param o the object to search for within the array
|
||||
* @return TRUE if the object exists, FALSE otherwise
|
||||
*/
|
||||
public boolean contains(Object o) {
|
||||
return array.contains(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Object> iterator() {
|
||||
return array.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON string (RFC 4627) containing this array
|
||||
*
|
||||
* @return a JSON string compatible with RFC 4627
|
||||
*/
|
||||
public String toString() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
new JSONOutputStream(baos).writeArray(this);
|
||||
} catch (IOException e) {
|
||||
return "Failed: " + e.getMessage();
|
||||
}
|
||||
return baos.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
public class JSONException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,296 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* This input stream will read RFC 4627 compatible JSON strings from either a string or an input
|
||||
* stream
|
||||
*/
|
||||
public class JSONInputStream extends InputStream {
|
||||
|
||||
private final InputStream is;
|
||||
private final byte[] buffer;
|
||||
private int bufferPos;
|
||||
private int bufferSize;
|
||||
private char peek;
|
||||
private boolean hasPeek;
|
||||
private boolean previousTokenString;
|
||||
|
||||
/**
|
||||
* Creates a new input stream around the specified string
|
||||
*
|
||||
* @param str a RFC 4627 JSON string
|
||||
*/
|
||||
public JSONInputStream(String str) {
|
||||
this(new ByteArrayInputStream(str.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new input stream around the specified input stream
|
||||
*
|
||||
* @param is the input stream pointing to the RFC 4627 JSON string
|
||||
*/
|
||||
public JSONInputStream(InputStream is) {
|
||||
this.is = is;
|
||||
this.buffer = new byte[256];
|
||||
this.bufferPos = 0;
|
||||
this.bufferSize = 0;
|
||||
this.peek = 0;
|
||||
this.hasPeek = false;
|
||||
this.previousTokenString = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a JSONObject from the stream
|
||||
*
|
||||
* @return the read JSONObject
|
||||
* @throws IOException if there is an exception within the input stream
|
||||
* @throws JSONException if there is a JSON parsing error
|
||||
*/
|
||||
public JSONObject readObject() throws IOException, JSONException {
|
||||
readAssert(getNextToken().equals("{"), "JSONObject must start with '{'");
|
||||
return getNextObjectInternal();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a JSONArray from the stream
|
||||
*
|
||||
* @return the read JSONArray
|
||||
* @throws IOException if there is an exception within the input stream
|
||||
* @throws JSONException if there is a JSON parsing error
|
||||
*/
|
||||
public JSONArray readArray() throws IOException, JSONException {
|
||||
readAssert(getNextToken().equals("["), "JSONArray must start with '['");
|
||||
return getNextArrayInternal();
|
||||
}
|
||||
|
||||
private JSONObject getNextObjectInternal() throws IOException, JSONException {
|
||||
JSONObject obj = new JSONObject();
|
||||
String token = getNextToken();
|
||||
while (!token.equals("}")) {
|
||||
String key = token;
|
||||
readAssert(getNextToken().equals(":"), "Attributes must be key-value pairs separated by ':'");
|
||||
token = getNextToken();
|
||||
if (previousTokenString)
|
||||
obj.put(key, token);
|
||||
else if (token.equals("null"))
|
||||
obj.putNull(key);
|
||||
else if (token.equals("false"))
|
||||
obj.put(key, false);
|
||||
else if (token.equals("true"))
|
||||
obj.put(key, true);
|
||||
else if (token.equals("["))
|
||||
obj.put(key, getNextArrayInternal());
|
||||
else if (token.equals("{"))
|
||||
obj.put(key, getNextObjectInternal());
|
||||
else {
|
||||
try {
|
||||
if (token.indexOf('.') != -1)
|
||||
obj.put(key, Double.valueOf(token));
|
||||
else
|
||||
obj.put(key, Long.valueOf(token));
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
token = getNextToken();
|
||||
if (!token.equals("}")) {
|
||||
readAssert(token.equals(","), "Attributes must be key-value pairs enumerated by ','");
|
||||
token = getNextToken();
|
||||
}
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
private JSONArray getNextArrayInternal() throws IOException, JSONException {
|
||||
JSONArray array = new JSONArray();
|
||||
String token = getNextToken();
|
||||
while (!token.equals("]")) {
|
||||
if (previousTokenString)
|
||||
array.add(token);
|
||||
else if (token.equals("null"))
|
||||
array.addNull();
|
||||
else if (token.equals("false"))
|
||||
array.add(false);
|
||||
else if (token.equals("true"))
|
||||
array.add(true);
|
||||
else if (token.equals("["))
|
||||
array.add(getNextArrayInternal());
|
||||
else if (token.equals("{"))
|
||||
array.add(getNextObjectInternal());
|
||||
else {
|
||||
try {
|
||||
if (token.indexOf('.') != -1)
|
||||
array.add(Double.valueOf(token));
|
||||
else
|
||||
array.add(Long.valueOf(token));
|
||||
} catch (NumberFormatException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
token = getNextToken();
|
||||
if (!token.equals("]")) {
|
||||
readAssert(token.equals(","), "Values must be enumerated by ','");
|
||||
token = getNextToken();
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
private String getNextToken() throws IOException, JSONException {
|
||||
char c = ingestWhitespace();
|
||||
previousTokenString = false;
|
||||
if (c == 0)
|
||||
return "";
|
||||
if (c == '{' || c == '[' || c == '}' || c == ']' || c == ',' || c == ':') {
|
||||
return Character.toString(c);
|
||||
}
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (c == '\"') {
|
||||
getNextTokenString(builder);
|
||||
previousTokenString = true;
|
||||
} else {
|
||||
builder.append(c);
|
||||
getNextTokenOther(builder);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void readAssert(boolean val, String message) throws JSONException {
|
||||
if (!val)
|
||||
throw new JSONException(message);
|
||||
}
|
||||
|
||||
private void getNextTokenString(StringBuilder builder) throws IOException, JSONException {
|
||||
char c = readChar();
|
||||
boolean prevEscape = false;
|
||||
while (c != '\"' || prevEscape) {
|
||||
if (c != '\\' || prevEscape)
|
||||
builder.append(c);
|
||||
prevEscape = (c == '\\' && !prevEscape);
|
||||
c = readChar();
|
||||
if (prevEscape) {
|
||||
switch (c) {
|
||||
case 'n':
|
||||
c = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
c = '\r';
|
||||
break;
|
||||
case 't':
|
||||
c = '\t';
|
||||
break;
|
||||
case 'b':
|
||||
c = '\b';
|
||||
break;
|
||||
case 'u':
|
||||
c = (char) Integer.valueOf("" + readChar() + readChar() + readChar() + readChar(), 16).intValue();
|
||||
break;
|
||||
case '\"':
|
||||
case '\\':
|
||||
break;
|
||||
default:
|
||||
throw new JSONException("Unknown escaped character: " + c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void getNextTokenOther(StringBuilder builder) throws IOException {
|
||||
while (isLetterOrNumber(peekChar())) {
|
||||
builder.append(readChar());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isLetterOrNumber(char c) {
|
||||
return isUppercase(c) || isLowercase(c) || isDigit(c) || isExtraNumCharacter(c);
|
||||
}
|
||||
|
||||
private boolean isUppercase(char c) {
|
||||
return c >= 'A' && c <= 'Z';
|
||||
}
|
||||
|
||||
private boolean isLowercase(char c) {
|
||||
return c >= 'a' && c <= 'z';
|
||||
}
|
||||
|
||||
private boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
private boolean isExtraNumCharacter(char c) {
|
||||
return c == '.' || c == '-' || c == 'E' || c == 'e' || c == '+';
|
||||
}
|
||||
|
||||
private boolean isWhitespace(char c) {
|
||||
return c == ' ' || c == '\n' || c == '\t' || c == '\r';
|
||||
}
|
||||
|
||||
private char ingestWhitespace() throws IOException {
|
||||
char c;
|
||||
do {
|
||||
c = readChar();
|
||||
} while (isWhitespace(c));
|
||||
return (char) c;
|
||||
}
|
||||
|
||||
public char peekChar() throws IOException {
|
||||
if (hasPeek)
|
||||
return peek;
|
||||
peek = readChar();
|
||||
hasPeek = true;
|
||||
return peek;
|
||||
}
|
||||
|
||||
public char readChar() throws IOException {
|
||||
if (hasPeek) {
|
||||
hasPeek = false;
|
||||
return peek;
|
||||
}
|
||||
int r = read();
|
||||
if (r == -1)
|
||||
return 0;
|
||||
return (char) r;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
if (bufferPos >= bufferSize) {
|
||||
bufferSize = read(buffer, 0, Math.min(buffer.length, available()));
|
||||
bufferPos = 0;
|
||||
if (bufferSize < 0)
|
||||
return -1;
|
||||
if (bufferSize == 0)
|
||||
return is.read();
|
||||
}
|
||||
return buffer[bufferPos++];
|
||||
}
|
||||
|
||||
public int read(byte[] b) throws IOException {
|
||||
return is.read(b);
|
||||
}
|
||||
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return is.read(b, off, len);
|
||||
}
|
||||
|
||||
public long skip(long n) throws IOException {
|
||||
return is.skip(n);
|
||||
}
|
||||
|
||||
public int available() throws IOException {
|
||||
return is.available();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
is.close();
|
||||
}
|
||||
|
||||
public void reset() throws IOException {
|
||||
is.reset();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,317 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InvalidClassException;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This class contains key-value pairs where the key is a string and the value is one of the
|
||||
* following types: JSONObject, JSONArray, Number, Boolean, String, or null
|
||||
*/
|
||||
public class JSONObject implements Map<String, Object> {
|
||||
|
||||
private final Map<String, Object> attributes;
|
||||
|
||||
public JSONObject() {
|
||||
attributes = new LinkedHashMap<String, Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of key-value pairs in the map
|
||||
*
|
||||
* @return the number of key-value pairs in the map
|
||||
*/
|
||||
public int size() {
|
||||
return attributes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the internal map of all key-value pairs
|
||||
*/
|
||||
public void clear() {
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the key-value pair from the map
|
||||
*
|
||||
* @param key the key to remove
|
||||
*/
|
||||
public Object remove(String key) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
return attributes.remove(key);
|
||||
}
|
||||
|
||||
public boolean containsKey(Object key) {
|
||||
return attributes.containsKey(key);
|
||||
}
|
||||
|
||||
public boolean containsValue(Object value) {
|
||||
return attributes.containsValue(value);
|
||||
}
|
||||
|
||||
public Object get(Object key) {
|
||||
return attributes.get(key);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return attributes.isEmpty();
|
||||
}
|
||||
|
||||
public Object put(String key, Object value) {
|
||||
if (value instanceof JSONObject || value instanceof JSONArray)
|
||||
return attributes.put(key, value);
|
||||
if (value instanceof Number || value instanceof Boolean)
|
||||
return attributes.put(key, value);
|
||||
if (value instanceof String)
|
||||
return attributes.put(key, value);
|
||||
if (value == null)
|
||||
return attributes.put(key, null);
|
||||
throw new IllegalArgumentException("Value must be of type: JSONObject, JSONArray, Number, Boolean, String, or null!");
|
||||
}
|
||||
|
||||
public void putAll(Map<? extends String, ? extends Object> m) {
|
||||
for (Entry<? extends String, ? extends Object> e : m.entrySet()) {
|
||||
put(e.getKey(), e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public Object remove(Object key) {
|
||||
return attributes.remove(key);
|
||||
}
|
||||
|
||||
public Set<Entry<String, Object>> entrySet() {
|
||||
return attributes.entrySet();
|
||||
}
|
||||
|
||||
public Set<String> keySet() {
|
||||
return attributes.keySet();
|
||||
}
|
||||
|
||||
public Collection<Object> values() {
|
||||
return attributes.values();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
return attributes.equals(o);
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return attributes.hashCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a JSONObject into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void put(String key, JSONObject value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a JSONArray into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void put(String key, JSONArray value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a number into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void put(String key, Number value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a boolean into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void put(String key, Boolean value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a string into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void put(String key, String value) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts a null value into the map with the specified key
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @param value the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public void putNull(String key) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
attributes.put(key, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value will always be a supported JSON
|
||||
* object: JSONObject, JSONArray, Number, Boolean, String, or null
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the value associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
*/
|
||||
public Object get(String key) {
|
||||
if (key == null)
|
||||
throw new NullPointerException("Key cannot be null!");
|
||||
|
||||
return attributes.get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a JSONObject
|
||||
* internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the JSONObject associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
* @throws InvalidClassException if the object is not a JSONObject
|
||||
*/
|
||||
public JSONObject getObject(String key) {
|
||||
return (JSONObject) get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a JSONArray
|
||||
* internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the JSONArray associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
* @throws InvalidClassException if the object is not a JSONArray
|
||||
*/
|
||||
public JSONArray getArray(String key) {
|
||||
return (JSONArray) get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a int internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the int associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null or if the value is null
|
||||
*/
|
||||
public int getInt(String key) {
|
||||
return ((Number) get(key)).intValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a long internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the long associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null or if the value is null
|
||||
*/
|
||||
public long getLong(String key) {
|
||||
return ((Number) get(key)).longValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a float internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the float associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null or if the value is null
|
||||
*/
|
||||
public float getFloat(String key) {
|
||||
return ((Number) get(key)).floatValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a double internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the double associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null or if the value is null
|
||||
*/
|
||||
public double getDouble(String key) {
|
||||
return ((Number) get(key)).doubleValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a boolean internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the boolean associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null or if the value is null
|
||||
* @throws InvalidClassException if the object is not a boolean
|
||||
*/
|
||||
public boolean getBoolean(String key) {
|
||||
return (boolean) get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value associated with the specified key. The value is casted to a String internally
|
||||
*
|
||||
* @param key the key for the map
|
||||
* @return the String associated with the specified key
|
||||
* @throws NullPointerException if the specified key is null
|
||||
* @throws InvalidClassException if the object is not a String
|
||||
*/
|
||||
public String getString(String key) {
|
||||
return (String) get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a JSON string (RFC 4627) containing this object
|
||||
*
|
||||
* @return a JSON string compatible with RFC 4627
|
||||
*/
|
||||
public String toString() {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
try {
|
||||
new JSONOutputStream(baos).writeObject(this);
|
||||
} catch (IOException e) {
|
||||
return "Failed: " + e.getMessage();
|
||||
}
|
||||
return baos.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,202 +0,0 @@
|
||||
package com.projectswg.installer.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* This output stream will write RFC 4627 JSON strings out
|
||||
*/
|
||||
public class JSONOutputStream extends OutputStream {
|
||||
|
||||
private final OutputStream os;
|
||||
private String indentation;
|
||||
private boolean compact;
|
||||
|
||||
/**
|
||||
* Wraps this JSON output stream around the specified output stream
|
||||
*
|
||||
* @param os the output stream to wrap
|
||||
*/
|
||||
public JSONOutputStream(OutputStream os) {
|
||||
this.os = os;
|
||||
this.indentation = " ";
|
||||
this.compact = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the indentation for each additional tab. This is not used if compact is set to true
|
||||
*
|
||||
* @param indentation the indentation for each tab
|
||||
* @see setCompact
|
||||
*/
|
||||
public void setIndentation(String indentation) {
|
||||
this.indentation = indentation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the mode to compact. If TRUE, there is no indentation or newlines
|
||||
*
|
||||
* @param compact TRUE to enable compact, FALSE otherwise
|
||||
*/
|
||||
public void setCompact(boolean compact) {
|
||||
this.compact = compact;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified JSONObject to the output stream
|
||||
*
|
||||
* @param obj the JSONObject to write
|
||||
* @throws IOException if there is an I/O error
|
||||
*/
|
||||
public void writeObject(JSONObject obj) throws IOException {
|
||||
writeObject(obj, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the specified JSONArray to the output stream
|
||||
*
|
||||
* @param array the JSONArray to write
|
||||
* @throws IOException if there is an I/O error
|
||||
*/
|
||||
public void writeArray(JSONArray array) throws IOException {
|
||||
writeArray(array, 0);
|
||||
}
|
||||
|
||||
private void writeObject(JSONObject obj, int depth) throws IOException {
|
||||
write('{');
|
||||
if (!compact)
|
||||
write('\n');
|
||||
int i = 0;
|
||||
Set<String> keys = obj.keySet();
|
||||
for (String key : keys) {
|
||||
if (!compact)
|
||||
writeIndentation(depth + 1);
|
||||
writeStringSafe("\"" + escapeString(key) + "\": ");
|
||||
writeValue(obj.get(key), depth + 1);
|
||||
if (i + 1 < keys.size())
|
||||
write(',');
|
||||
if (!compact)
|
||||
write('\n');
|
||||
i++;
|
||||
}
|
||||
if (!compact)
|
||||
writeIndentation(depth);
|
||||
write('}');
|
||||
}
|
||||
|
||||
private void writeArray(JSONArray array, int depth) throws IOException {
|
||||
write('[');
|
||||
if (!compact)
|
||||
write('\n');
|
||||
for (int i = 0; i < array.size(); i++) {
|
||||
if (!compact)
|
||||
writeIndentation(depth + 1);
|
||||
Object o = array.get(i);
|
||||
writeValue(o, depth + 1);
|
||||
if (i + 1 < array.size())
|
||||
write(',');
|
||||
if (!compact)
|
||||
write('\n');
|
||||
}
|
||||
if (!compact)
|
||||
writeIndentation(depth);
|
||||
write(']');
|
||||
}
|
||||
|
||||
private void writeValue(Object o, int depth) throws IOException {
|
||||
if (o instanceof String) // String
|
||||
writeStringSafe("\"" + escapeString((String) o) + "\"");
|
||||
else if (o instanceof Number) // Number
|
||||
writeNumber((Number) o);
|
||||
else if (o instanceof Boolean) // Boolean
|
||||
writeString(o.toString());
|
||||
else if (o == null) // Null
|
||||
writeString("null");
|
||||
else if (o instanceof JSONObject) // Object
|
||||
writeObject(((JSONObject) o), depth);
|
||||
else if (o instanceof JSONArray) // Array
|
||||
writeArray(((JSONArray) o), depth);
|
||||
}
|
||||
|
||||
private void writeNumber(Number n) throws IOException {
|
||||
if (n instanceof Double && (Double.isNaN((Double) n) || Double.isInfinite((Double) n))) {
|
||||
write('0');
|
||||
return;
|
||||
}
|
||||
if (n instanceof Float && (Float.isNaN((Float) n) || Float.isInfinite((Float) n))) {
|
||||
write('0');
|
||||
return;
|
||||
}
|
||||
writeString(n.toString());
|
||||
}
|
||||
|
||||
private void writeIndentation(int depth) throws IOException {
|
||||
for (int i = 0; i < depth; ++i)
|
||||
writeString(indentation);
|
||||
}
|
||||
|
||||
private void writeString(String str) throws IOException {
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
write(str.charAt(i));
|
||||
}
|
||||
|
||||
private void writeStringSafe(String str) throws IOException {
|
||||
write(str.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private String escapeString(String str) {
|
||||
StringBuilder builder = new StringBuilder(str.length());
|
||||
char c;
|
||||
for (int i = 0; i < str.length(); ++i) {
|
||||
c = str.charAt(i);
|
||||
if (c == '\\' || c == '\"') {
|
||||
builder.append('\\');
|
||||
builder.append(c);
|
||||
} else if (c <= 0x1F) {
|
||||
builder.append('\\');
|
||||
switch (c) {
|
||||
case '\n':
|
||||
builder.append('n');
|
||||
break;
|
||||
case '\r':
|
||||
builder.append('r');
|
||||
break;
|
||||
case '\t':
|
||||
builder.append('t');
|
||||
break;
|
||||
case '\b':
|
||||
builder.append('b');
|
||||
break;
|
||||
default:
|
||||
builder.append(String.format("u%04X", (int) c));
|
||||
break;
|
||||
}
|
||||
} else
|
||||
builder.append(c);
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
os.write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b) throws IOException {
|
||||
os.write(b);
|
||||
}
|
||||
|
||||
public void write(byte[] b, int off, int len) throws IOException {
|
||||
os.write(b, off, len);
|
||||
}
|
||||
|
||||
public void flush() throws IOException {
|
||||
os.flush();
|
||||
}
|
||||
|
||||
public void close() throws IOException {
|
||||
os.close();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user