Initial commit with BSL
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* OtterTune - ControllerConfiguration.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller;
|
||||
|
||||
/** Controller Configuration. */
|
||||
public class ControllerConfiguration {
|
||||
private DatabaseType dbType;
|
||||
private String dbName;
|
||||
private String dbUsername;
|
||||
private String dbPassword;
|
||||
private String dbURL;
|
||||
private String uploadCode;
|
||||
private String uploadURL;
|
||||
private String workloadName;
|
||||
|
||||
public ControllerConfiguration() {}
|
||||
|
||||
public ControllerConfiguration(
|
||||
String dbName,
|
||||
String dbUsername,
|
||||
String dbPassword,
|
||||
String dbURL,
|
||||
String uploadCode,
|
||||
String uploadURL,
|
||||
String workloadName) {
|
||||
this.dbType = DatabaseType.get(dbName);
|
||||
this.dbName = dbName;
|
||||
this.dbUsername = dbUsername;
|
||||
this.dbPassword = dbPassword;
|
||||
this.dbURL = dbURL;
|
||||
this.uploadCode = uploadCode;
|
||||
this.uploadURL = uploadURL;
|
||||
this.workloadName = workloadName;
|
||||
}
|
||||
|
||||
/* Mutators */
|
||||
public void setDBType(DatabaseType dbType) {
|
||||
this.dbType = dbType;
|
||||
}
|
||||
|
||||
public void setDBName(String dbName) {
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
public void setDBUsername(String dbUsername) {
|
||||
this.dbUsername = dbUsername;
|
||||
}
|
||||
|
||||
public void setPassword(String dbPassword) {
|
||||
this.dbPassword = dbPassword;
|
||||
}
|
||||
|
||||
public void setDBURL(String dbURL) {
|
||||
this.dbURL = dbURL;
|
||||
}
|
||||
|
||||
public void setUploadCode(String uploadCode) {
|
||||
this.uploadCode = uploadCode;
|
||||
}
|
||||
|
||||
public void setUploadURL(String uploadURL) {
|
||||
this.uploadURL = uploadURL;
|
||||
}
|
||||
|
||||
public void setWorkloadName(String workloadName) {
|
||||
this.workloadName = workloadName;
|
||||
}
|
||||
|
||||
/* Getters */
|
||||
public DatabaseType getDBType() {
|
||||
return this.dbType;
|
||||
}
|
||||
|
||||
public String getDBName() {
|
||||
return this.dbName;
|
||||
}
|
||||
|
||||
public String getDBUsername() {
|
||||
return this.dbUsername;
|
||||
}
|
||||
|
||||
public String getDBPassword() {
|
||||
return this.dbPassword;
|
||||
}
|
||||
|
||||
public String getDBURL() {
|
||||
return this.dbURL;
|
||||
}
|
||||
|
||||
public String getUploadCode() {
|
||||
return this.uploadCode;
|
||||
}
|
||||
|
||||
public String getUploadURL() {
|
||||
return this.uploadURL;
|
||||
}
|
||||
|
||||
public String getWorkloadName() {
|
||||
return this.workloadName;
|
||||
}
|
||||
}
|
||||
296
client/controller/src/main/java/com/controller/Main.java
Normal file
296
client/controller/src/main/java/com/controller/Main.java
Normal file
@@ -0,0 +1,296 @@
|
||||
/*
|
||||
* OtterTune - Main.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller;
|
||||
|
||||
import com.controller.collectors.DBCollector;
|
||||
import com.controller.collectors.MySQLCollector;
|
||||
import com.controller.collectors.OracleCollector;
|
||||
import com.controller.collectors.PostgresCollector;
|
||||
import com.controller.collectors.SAPHanaCollector;
|
||||
import com.controller.types.JSONSchemaType;
|
||||
import com.controller.util.FileUtil;
|
||||
import com.controller.util.JSONUtil;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import org.apache.commons.cli.CommandLine;
|
||||
import org.apache.commons.cli.CommandLineParser;
|
||||
import org.apache.commons.cli.HelpFormatter;
|
||||
import org.apache.commons.cli.Options;
|
||||
import org.apache.commons.cli.ParseException;
|
||||
import org.apache.commons.cli.PosixParser;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.log4j.PropertyConfigurator;
|
||||
import sun.misc.Signal;
|
||||
|
||||
/**
|
||||
* Controller main.
|
||||
*
|
||||
* @author Shuli
|
||||
*/
|
||||
public class Main {
|
||||
static final Logger LOG = Logger.getLogger(Main.class);
|
||||
|
||||
// Default output directory name
|
||||
private static final String DEFAULT_DIRECTORY = "output";
|
||||
|
||||
// Default observation period time (5 minutes)
|
||||
private static final int DEFAULT_TIME_SECONDS = -1;
|
||||
|
||||
// Path to JSON schema directory
|
||||
private static final String SCHEMA_PATH = "src/main/java/com/controller/json_validation_schema";
|
||||
|
||||
private static final int TO_MILLISECONDS = 1000;
|
||||
|
||||
private static boolean keepRunning = true;
|
||||
private static boolean firstCollecting = false;
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
// Initialize log4j
|
||||
PropertyConfigurator.configure("log4j.properties");
|
||||
|
||||
// Initialize keepRunning
|
||||
keepRunning = true;
|
||||
|
||||
// Initialize firstCollecting
|
||||
firstCollecting = false;
|
||||
|
||||
// Create the command line parser
|
||||
CommandLineParser parser = new PosixParser();
|
||||
Options options = new Options();
|
||||
options.addOption("c", "config", true, "[required] Controller configuration file");
|
||||
options.addOption("t", "time", true, "The observation time in seconds, default is 300s");
|
||||
options.addOption(
|
||||
"d", "directory", true, "Base directory for the result files, default is 'output'");
|
||||
options.addOption("h", "help", true, "Print this help");
|
||||
String configFilePath = null;
|
||||
|
||||
// Parse the command line arguments
|
||||
CommandLine argsLine;
|
||||
try {
|
||||
argsLine = parser.parse(options, args);
|
||||
} catch (ParseException e) {
|
||||
LOG.error("Unable to Parse command line arguments");
|
||||
printUsage(options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (argsLine.hasOption("h")) {
|
||||
printUsage(options);
|
||||
return;
|
||||
} else if (argsLine.hasOption("c") == false) {
|
||||
LOG.error("Missing configuration file");
|
||||
printUsage(options);
|
||||
return;
|
||||
}
|
||||
|
||||
int time = DEFAULT_TIME_SECONDS;
|
||||
if (argsLine.hasOption("t")) {
|
||||
time = Integer.parseInt(argsLine.getOptionValue("t"));
|
||||
}
|
||||
LOG.info("Experiment time is set to: " + time);
|
||||
|
||||
String outputDirectory = DEFAULT_DIRECTORY;
|
||||
if (argsLine.hasOption("d")) {
|
||||
outputDirectory = argsLine.getOptionValue("d");
|
||||
}
|
||||
LOG.info("Experiment output directory is set to: " + outputDirectory);
|
||||
FileUtil.makeDirIfNotExists(outputDirectory);
|
||||
|
||||
// Parse controller configuration file
|
||||
String configPath = argsLine.getOptionValue("c");
|
||||
File configFile = new File(configPath);
|
||||
|
||||
// Check config format
|
||||
if (!JSONSchemaType.isValidJson(JSONSchemaType.CONFIG, configFile)) {
|
||||
LOG.error("Invalid configuration JSON format");
|
||||
return;
|
||||
}
|
||||
|
||||
// Load configuration file
|
||||
ControllerConfiguration config = null;
|
||||
try {
|
||||
JSONObject input = new JSONObject(FileUtil.readFile(configFile));
|
||||
config =
|
||||
new ControllerConfiguration(
|
||||
input.getString("database_type"),
|
||||
input.getString("username"),
|
||||
input.getString("password"),
|
||||
input.getString("database_url"),
|
||||
input.getString("upload_code"),
|
||||
input.getString("upload_url"),
|
||||
input.getString("workload_name"));
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
DBCollector collector = getCollector(config);
|
||||
try {
|
||||
// add a signal handler
|
||||
Signal.handle(new Signal("INT"), signal -> firstCollecting = true);
|
||||
File f = new File("pid.txt");
|
||||
|
||||
// get pid of this process and write the pid to a file before recording the start time
|
||||
if (time < 0) {
|
||||
String vmName = ManagementFactory.getRuntimeMXBean().getName();
|
||||
int p = vmName.indexOf("@");
|
||||
int pid = Integer.valueOf(vmName.substring(0, p));
|
||||
try {
|
||||
f.createNewFile();
|
||||
PrintWriter pidWriter = new PrintWriter(f);
|
||||
pidWriter.println(pid);
|
||||
pidWriter.flush();
|
||||
pidWriter.close();
|
||||
} catch (IOException ioe) {
|
||||
ioe.printStackTrace();
|
||||
}
|
||||
}
|
||||
LOG.info("Output the process pid to pid.txt");
|
||||
|
||||
while (!firstCollecting) {
|
||||
Thread.sleep(1);
|
||||
}
|
||||
|
||||
// first collection (before queries)
|
||||
LOG.info("First collection of metrics before experiment");
|
||||
String metricsBefore = collector.collectMetrics();
|
||||
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, metricsBefore)) {
|
||||
LOG.error("Invalid output JSON format (metrics_before)");
|
||||
return;
|
||||
}
|
||||
PrintWriter metricsWriter =
|
||||
new PrintWriter(FileUtil.joinPath(outputDirectory, "metrics_before.json"), "UTF-8");
|
||||
metricsWriter.println(metricsBefore);
|
||||
metricsWriter.close();
|
||||
|
||||
String knobs = collector.collectParameters();
|
||||
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, knobs)) {
|
||||
LOG.error("Invalid output JSON format (knobs)");
|
||||
return;
|
||||
}
|
||||
PrintWriter knobsWriter =
|
||||
new PrintWriter(FileUtil.joinPath(outputDirectory, "knobs.json"), "UTF-8");
|
||||
knobsWriter.println(knobs);
|
||||
knobsWriter.close();
|
||||
|
||||
// add a signal handler
|
||||
Signal.handle(new Signal("INT"), signal -> keepRunning = false);
|
||||
|
||||
// record start time
|
||||
long startTime = System.currentTimeMillis();
|
||||
LOG.info("Starting the experiment ...");
|
||||
|
||||
// go to sleep
|
||||
if (time >= 0) {
|
||||
Thread.sleep(time * TO_MILLISECONDS);
|
||||
} else {
|
||||
while (keepRunning) {
|
||||
Thread.sleep(1);
|
||||
}
|
||||
f.delete();
|
||||
}
|
||||
|
||||
long endTime = System.currentTimeMillis();
|
||||
long observationTime = time >= 0 ? time : (endTime - startTime) / TO_MILLISECONDS;
|
||||
LOG.info("Done running the experiment");
|
||||
|
||||
// summary json obj
|
||||
JSONObject summary = null;
|
||||
try {
|
||||
summary = new JSONObject();
|
||||
summary.put("start_time", startTime);
|
||||
summary.put("end_time", endTime);
|
||||
summary.put("observation_time", observationTime);
|
||||
summary.put("database_type", config.getDBName());
|
||||
summary.put("database_version", collector.collectVersion());
|
||||
summary.put("workload_name", config.getWorkloadName());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (!JSONSchemaType.isValidJson(JSONSchemaType.SUMMARY, summary.toString())) {
|
||||
LOG.error("Invalid summary JSON format");
|
||||
return;
|
||||
}
|
||||
|
||||
// write summary JSONObject into a JSON file
|
||||
PrintWriter summaryout =
|
||||
new PrintWriter(FileUtil.joinPath(outputDirectory, "summary.json"), "UTF-8");
|
||||
summaryout.println(JSONUtil.format(summary.toString()));
|
||||
summaryout.close();
|
||||
|
||||
// second collection (after workload execution)
|
||||
LOG.info("Second collection of metrics after experiment");
|
||||
collector = getCollector(config);
|
||||
String metricsAfter = collector.collectMetrics();
|
||||
if (!JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, metricsAfter)) {
|
||||
LOG.error("Invalid output JSON format (metrics_after)");
|
||||
return;
|
||||
}
|
||||
PrintWriter metricsWriterFinal =
|
||||
new PrintWriter(FileUtil.joinPath(outputDirectory, "metrics_after.json"), "UTF-8");
|
||||
metricsWriterFinal.println(metricsAfter);
|
||||
metricsWriterFinal.close();
|
||||
} catch (FileNotFoundException | UnsupportedEncodingException | InterruptedException e) {
|
||||
LOG.error("Failed to produce output files");
|
||||
e.printStackTrace();
|
||||
}
|
||||
if (config.getUploadURL() != null && !config.getUploadURL().equals("")) {
|
||||
Map<String, String> outfiles = new HashMap<>();
|
||||
outfiles.put("knobs", FileUtil.joinPath(outputDirectory, "knobs.json"));
|
||||
outfiles.put("metrics_before", FileUtil.joinPath(outputDirectory, "metrics_before.json"));
|
||||
outfiles.put("metrics_after", FileUtil.joinPath(outputDirectory, "metrics_after.json"));
|
||||
outfiles.put("summary", FileUtil.joinPath(outputDirectory, "summary.json"));
|
||||
try {
|
||||
ResultUploader.upload(config.getUploadURL(), config.getUploadCode(), outfiles);
|
||||
} catch (IOException ioe) {
|
||||
LOG.warn("Failed to upload results from the controller");
|
||||
}
|
||||
} else {
|
||||
LOG.warn("Empty upload URL. Skipping upload...");
|
||||
}
|
||||
}
|
||||
|
||||
private static void printUsage(Options options) {
|
||||
HelpFormatter formatter = new HelpFormatter();
|
||||
formatter.printHelp("controller", options);
|
||||
}
|
||||
|
||||
private static DBCollector getCollector(ControllerConfiguration config) {
|
||||
DBCollector collector = null;
|
||||
switch (config.getDBType()) {
|
||||
case POSTGRES:
|
||||
collector =
|
||||
new PostgresCollector(
|
||||
config.getDBURL(), config.getDBUsername(), config.getDBPassword());
|
||||
break;
|
||||
case MYSQL:
|
||||
collector =
|
||||
new MySQLCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
|
||||
break;
|
||||
case SAPHANA:
|
||||
collector =
|
||||
new SAPHanaCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
|
||||
break;
|
||||
case ORACLE:
|
||||
collector =
|
||||
new OracleCollector(config.getDBURL(), config.getDBUsername(), config.getDBPassword());
|
||||
break;
|
||||
default:
|
||||
LOG.error("Invalid database type");
|
||||
throw new RuntimeException("Invalid database type");
|
||||
}
|
||||
return collector;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* OtterTune - ResultUploader.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import org.apache.http.HttpEntity;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.entity.mime.MultipartEntityBuilder;
|
||||
import org.apache.http.entity.mime.content.FileBody;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.util.EntityUtils;
|
||||
|
||||
/**
|
||||
* Uploading the result.
|
||||
*
|
||||
* @author Shuli
|
||||
*/
|
||||
public class ResultUploader {
|
||||
public static void upload(String uploadURL, String uploadCode,
|
||||
Map<String, String> files) throws IOException {
|
||||
|
||||
try {
|
||||
List<String> filesToSendNames = new ArrayList<>();
|
||||
List<File> filesToSend = new ArrayList<>();
|
||||
for (String fileName : files.keySet()) {
|
||||
String path = files.get(fileName);
|
||||
filesToSendNames.add(fileName);
|
||||
File f = new File(path);
|
||||
filesToSend.add(f);
|
||||
}
|
||||
CloseableHttpClient httpclient = HttpClients.createDefault();
|
||||
HttpPost httppost = new HttpPost(uploadURL);
|
||||
MultipartEntityBuilder mb =
|
||||
MultipartEntityBuilder.create().addTextBody("upload_code", uploadCode);
|
||||
for (int i = 0; i < filesToSendNames.size(); i++) {
|
||||
mb.addPart(filesToSendNames.get(i), new FileBody(filesToSend.get(i)));
|
||||
}
|
||||
|
||||
HttpEntity reqEntity = mb.build();
|
||||
httppost.setEntity(reqEntity);
|
||||
CloseableHttpResponse response = httpclient.execute(httppost);
|
||||
try {
|
||||
HttpEntity resEntity = response.getEntity();
|
||||
EntityUtils.consume(resEntity);
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* OtterTune - DBCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.util.JSONUtil;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class DBCollector implements DBParameterCollector {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DBCollector.class);
|
||||
|
||||
protected static final String JSON_GLOBAL_KEY = "global";
|
||||
protected static final String JSON_LOCAL_KEY = "local";
|
||||
|
||||
protected final Map<String, String> dbParameters = new TreeMap<String, String>();
|
||||
|
||||
protected final Map<String, String> dbMetrics = new TreeMap<String, String>();
|
||||
|
||||
protected final StringBuilder version = new StringBuilder();
|
||||
|
||||
@Override
|
||||
public boolean hasParameters() {
|
||||
return (dbParameters.isEmpty() == false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMetrics() {
|
||||
return (dbMetrics.isEmpty() == false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectParameters() {
|
||||
return JSONUtil.format(JSONUtil.toJSONString(dbParameters));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectMetrics() {
|
||||
return JSONUtil.format(JSONUtil.toJSONString(dbMetrics));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectVersion() {
|
||||
return version.toString();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* OtterTune - DBParameterCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
public interface DBParameterCollector {
|
||||
boolean hasParameters();
|
||||
|
||||
boolean hasMetrics();
|
||||
|
||||
String collectParameters();
|
||||
|
||||
String collectMetrics();
|
||||
|
||||
String collectVersion();
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* OtterTune - MySQLCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.util.JSONUtil;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/** */
|
||||
public class MySQLCollector extends DBCollector {
|
||||
private static final Logger LOG = Logger.getLogger(MySQLCollector.class);
|
||||
|
||||
private static final String VERSION_SQL = "SELECT @@GLOBAL.version;";
|
||||
|
||||
private static final String PARAMETERS_SQL = "SHOW VARIABLES;";
|
||||
|
||||
private static final String METRICS_SQL = "SHOW STATUS";
|
||||
|
||||
public MySQLCollector(String oriDBUrl, String username, String password) {
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
|
||||
Statement s = conn.createStatement();
|
||||
|
||||
// Collect DBMS version
|
||||
ResultSet out = s.executeQuery(VERSION_SQL);
|
||||
if (out.next()) {
|
||||
this.version.append(out.getString(1));
|
||||
}
|
||||
|
||||
// Collect DBMS parameters
|
||||
out = s.executeQuery(PARAMETERS_SQL);
|
||||
while (out.next()) {
|
||||
dbParameters.put(out.getString(1).toLowerCase(), out.getString(2));
|
||||
}
|
||||
|
||||
// Collect DBMS internal metrics
|
||||
out = s.executeQuery(METRICS_SQL);
|
||||
while (out.next()) {
|
||||
dbMetrics.put(out.getString(1).toLowerCase(), out.getString(2));
|
||||
}
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
LOG.error("Error while collecting DB parameters: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectParameters() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (String k : dbParameters.keySet()) {
|
||||
job.put(k, dbParameters.get(k));
|
||||
}
|
||||
// "global is a fake view_name (a placeholder)"
|
||||
jobLocal.put("global", job);
|
||||
stringer.value(jobLocal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectMetrics() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobGlobal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (Map.Entry<String, String> entry : dbMetrics.entrySet()) {
|
||||
job.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
// "global" is a a placeholder
|
||||
jobGlobal.put("global", job);
|
||||
stringer.value(jobGlobal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* OtterTune - OracleCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.util.JSONUtil;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.Map;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/** */
|
||||
public class OracleCollector extends DBCollector {
|
||||
private static final Logger LOG = Logger.getLogger(MySQLCollector.class);
|
||||
|
||||
private static final String VERSION_SQL = "select VERSION from product_component_version";
|
||||
|
||||
private static final String PARAMETERS_SQL = "select name, value from v$parameter";
|
||||
|
||||
private static final String PARAMETERS_SQL_WITH_HIDDEN =
|
||||
"select x.ksppinm name, y.ksppstvl value from sys.x$ksppi x, sys.x$ksppcv y where"
|
||||
+ " x.inst_id = userenv('Instance') and y.inst_id = userenv('Instance') and x.indx = y.indx";
|
||||
|
||||
private static final String METRICS_SQL = "select name, value from v$sysstat";
|
||||
|
||||
public OracleCollector(String oriDBUrl, String username, String password) {
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
|
||||
Statement statement = conn.createStatement();
|
||||
// Collect DBMS version
|
||||
ResultSet out = statement.executeQuery(VERSION_SQL);
|
||||
if (out.next()) {
|
||||
this.version.append(out.getString(1));
|
||||
}
|
||||
|
||||
// Collect DBMS parameters
|
||||
out = statement.executeQuery(PARAMETERS_SQL_WITH_HIDDEN);
|
||||
while (out.next()) {
|
||||
dbParameters.put(out.getString(1).toLowerCase(), out.getString(2));
|
||||
}
|
||||
|
||||
// Collect DBMS internal metrics
|
||||
out = statement.executeQuery(METRICS_SQL);
|
||||
while (out.next()) {
|
||||
dbMetrics.put(out.getString(1).toLowerCase(), out.getString(2));
|
||||
}
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
LOG.error("Error while collecting DB parameters: " + e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectParameters() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (Map.Entry<String, String> entry : dbParameters.entrySet()) {
|
||||
job.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
// "global is a fake view_name (a placeholder)"
|
||||
jobLocal.put("global", job);
|
||||
stringer.value(jobLocal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectMetrics() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobGlobal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (Map.Entry<String, String> entry : dbMetrics.entrySet()) {
|
||||
job.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
// "global" is a placeholder
|
||||
jobGlobal.put("global", job);
|
||||
stringer.value(jobGlobal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* OtterTune - PostgresCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.util.JSONUtil;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class PostgresCollector extends DBCollector {
|
||||
private static final Logger LOG = Logger.getLogger(PostgresCollector.class);
|
||||
|
||||
private static final String VERSION_SQL = "SELECT version();";
|
||||
|
||||
private static final String PARAMETERS_SQL = "SHOW ALL;";
|
||||
|
||||
private boolean oldVersion = false;
|
||||
|
||||
private static final String[] PG_STAT_VIEWS = {
|
||||
"pg_stat_archiver", "pg_stat_bgwriter", "pg_stat_database",
|
||||
"pg_stat_database_conflicts", "pg_stat_user_tables", "pg_statio_user_tables",
|
||||
"pg_stat_user_indexes", "pg_statio_user_indexes"
|
||||
};
|
||||
|
||||
private static final String[] PG_STAT_VIEWS_OLD_VERSION = {
|
||||
"pg_stat_bgwriter", "pg_stat_database",
|
||||
"pg_stat_database_conflicts", "pg_stat_user_tables", "pg_statio_user_tables",
|
||||
"pg_stat_user_indexes", "pg_statio_user_indexes"
|
||||
};
|
||||
|
||||
private static final String[] PG_STAT_VIEWS_LOCAL_DATABASE = {
|
||||
"pg_stat_database", "pg_stat_database_conflicts"
|
||||
};
|
||||
private static final String PG_STAT_VIEWS_LOCAL_DATABASE_KEY = "datname";
|
||||
private static final String[] PG_STAT_VIEWS_LOCAL_TABLE = {
|
||||
"pg_stat_user_tables", "pg_statio_user_tables"
|
||||
};
|
||||
private static final String PG_STAT_VIEWS_LOCAL_TABLE_KEY = "relname";
|
||||
private static final String[] PG_STAT_VIEWS_LOCAL_INDEXES = {
|
||||
"pg_stat_user_indexes", "pg_statio_user_indexes"
|
||||
};
|
||||
private static final String PG_STAT_VIEWS_LOCAL_INDEXES_KEY = "relname";
|
||||
|
||||
private final Map<String, List<Map<String, String>>> pgMetrics;
|
||||
|
||||
public PostgresCollector(String oriDBUrl, String username, String password) {
|
||||
pgMetrics = new HashMap<>();
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
|
||||
|
||||
Statement s = conn.createStatement();
|
||||
|
||||
// Collect DBMS version
|
||||
ResultSet out = s.executeQuery(VERSION_SQL);
|
||||
if (out.next()) {
|
||||
String[] outStr = out.getString(1).split(" ");
|
||||
String[] verStr = outStr[1].split("\\.");
|
||||
this.version.append(verStr[0]);
|
||||
this.version.append(".");
|
||||
this.version.append(verStr[1]);
|
||||
}
|
||||
|
||||
// Collect DBMS parameters
|
||||
out = s.executeQuery(PARAMETERS_SQL);
|
||||
while (out.next()) {
|
||||
dbParameters.put(out.getString("name"), out.getString("setting"));
|
||||
}
|
||||
|
||||
// Collect DBMS internal metrics
|
||||
String[] pgStatViews = PG_STAT_VIEWS;
|
||||
if (Float.parseFloat(this.version.toString()) < 9.4) {
|
||||
this.oldVersion = true;
|
||||
pgStatViews = PG_STAT_VIEWS_OLD_VERSION;
|
||||
}
|
||||
|
||||
for (String viewName : pgStatViews) {
|
||||
out = s.executeQuery("SELECT * FROM " + viewName);
|
||||
pgMetrics.put(viewName, getMetrics(out));
|
||||
}
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Error while collecting DB parameters: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMetrics() {
|
||||
return (pgMetrics.isEmpty() == false);
|
||||
}
|
||||
|
||||
private JSONObject genMapJSONObj(Map<String, String> mapin) {
|
||||
JSONObject res = new JSONObject();
|
||||
try {
|
||||
for (String key : mapin.keySet()) {
|
||||
res.put(key, mapin.get(key));
|
||||
}
|
||||
} catch (JSONException je) {
|
||||
LOG.error(je);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private JSONObject genLocalJSONObj(String viewName, String jsonKeyName) {
|
||||
JSONObject thisViewObj = new JSONObject();
|
||||
List<Map<String, String>> thisViewList = pgMetrics.get(viewName);
|
||||
try {
|
||||
for (Map<String, String> dbmap : thisViewList) {
|
||||
String jsonkey = dbmap.get(jsonKeyName);
|
||||
thisViewObj.put(jsonkey, genMapJSONObj(dbmap));
|
||||
}
|
||||
} catch (JSONException je) {
|
||||
LOG.error(je);
|
||||
}
|
||||
return thisViewObj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectMetrics() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
// create global objects for two views: "pg_stat_archiver" and "pg_stat_bgwriter"
|
||||
JSONObject jobGlobal = new JSONObject();
|
||||
// "pg_stat_archiver" (only one instance in the list) >= version 9.4
|
||||
if (!this.oldVersion) {
|
||||
Map<String, String> archiverList = pgMetrics.get("pg_stat_archiver").get(0);
|
||||
jobGlobal.put("pg_stat_archiver", genMapJSONObj(archiverList));
|
||||
}
|
||||
|
||||
// "pg_stat_bgwriter" (only one instance in the list)
|
||||
Map<String, String> bgwriterList = pgMetrics.get("pg_stat_bgwriter").get(0);
|
||||
jobGlobal.put("pg_stat_bgwriter", genMapJSONObj(bgwriterList));
|
||||
|
||||
// add global json object
|
||||
stringer.value(jobGlobal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
// create local objects for the rest of the views
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
|
||||
// "table"
|
||||
JSONObject jobTable = new JSONObject();
|
||||
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_TABLE.length; i++) {
|
||||
String viewName = PG_STAT_VIEWS_LOCAL_TABLE[i];
|
||||
String jsonKeyName = PG_STAT_VIEWS_LOCAL_TABLE_KEY;
|
||||
jobTable.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
jobLocal.put("table", jobTable);
|
||||
|
||||
// "database"
|
||||
JSONObject jobDatabase = new JSONObject();
|
||||
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_DATABASE.length; i++) {
|
||||
String viewName = PG_STAT_VIEWS_LOCAL_DATABASE[i];
|
||||
String jsonKeyName = PG_STAT_VIEWS_LOCAL_DATABASE_KEY;
|
||||
jobDatabase.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
jobLocal.put("database", jobDatabase);
|
||||
|
||||
// "indexes"
|
||||
JSONObject jobIndexes = new JSONObject();
|
||||
for (int i = 0; i < PG_STAT_VIEWS_LOCAL_INDEXES.length; i++) {
|
||||
String viewName = PG_STAT_VIEWS_LOCAL_INDEXES[i];
|
||||
String jsonKeyName = PG_STAT_VIEWS_LOCAL_INDEXES_KEY;
|
||||
jobIndexes.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
jobLocal.put("indexes", jobIndexes);
|
||||
|
||||
// add local json object
|
||||
stringer.value(jobLocal);
|
||||
stringer.endObject();
|
||||
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
|
||||
private static List<Map<String, String>> getMetrics(ResultSet out) throws SQLException {
|
||||
ResultSetMetaData metadata = out.getMetaData();
|
||||
int numColumns = metadata.getColumnCount();
|
||||
String[] columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; ++i) {
|
||||
columnNames[i] = metadata.getColumnName(i + 1).toLowerCase();
|
||||
}
|
||||
|
||||
List<Map<String, String>> metrics = new ArrayList<Map<String, String>>();
|
||||
while (out.next()) {
|
||||
Map<String, String> metricMap = new TreeMap<String, String>();
|
||||
for (int i = 0; i < numColumns; ++i) {
|
||||
metricMap.put(columnNames[i], out.getString(i + 1));
|
||||
}
|
||||
metrics.add(metricMap);
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectParameters() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (String k : dbParameters.keySet()) {
|
||||
job.put(k, dbParameters.get(k));
|
||||
}
|
||||
// "global is a fake view_name (a placeholder)"
|
||||
jobLocal.put("global", job);
|
||||
stringer.value(jobLocal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* OtterTune - SAPHanaCollector.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.util.JSONUtil;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.ResultSetMetaData;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
public class SAPHanaCollector extends DBCollector {
|
||||
private static final Logger LOG = Logger.getLogger(SAPHanaCollector.class);
|
||||
|
||||
private static final String VERSION_SQL = "SELECT VERSION from M_DATABASE";
|
||||
|
||||
private static final String PARAMETERS_SQL = "Select * from M_INIFILE_CONTENTS";
|
||||
|
||||
private static final String[] SAP_SYS_VIEW = {
|
||||
"m_host_agent_metrics",
|
||||
"m_caches",
|
||||
"m_disk_usage",
|
||||
"m_garbage_collection_statistics",
|
||||
"m_host_resource_utilization",
|
||||
"m_data_volumes"
|
||||
};
|
||||
|
||||
private static final String[] SAP_SYS_LOCAL_VIEW = {"m_table_statistics", "m_rs_indexes"};
|
||||
private static final String[] SAP_SYS_VIEW_GLOBAL = {
|
||||
"m_host_agent_metrics",
|
||||
"m_caches",
|
||||
"m_disk_usage",
|
||||
"m_garbage_collection_statistics",
|
||||
"m_host_resource_utilization",
|
||||
"m_data_volumes"
|
||||
};
|
||||
private static final String[] SAP_SYS_VIEW_GLOBAL_KEY = {
|
||||
"instance_id", "cache_id", "usage_type", "store_type", "host", "volume_id"
|
||||
};
|
||||
private static final String[] SAP_SYS_VIEW_LOCAL_TABLE = {"m_table_statistics"};
|
||||
private static final String[] SAP_SYS_VIEW_LOCAL_TABLE_KEY = {"table_name"};
|
||||
private static final String[] SAP_SYS_VIEW_LOCAL_INDEXES = {"m_rs_indexes"};
|
||||
private static final String[] SAP_SYS_VIEW_LOCAL_INDEXES_KEY = {"index_name"};
|
||||
|
||||
private final Map<String, List<Map<String, String>>> pgMetrics;
|
||||
|
||||
public SAPHanaCollector(String oriDBUrl, String username, String password) {
|
||||
pgMetrics = new HashMap<>();
|
||||
try {
|
||||
Connection conn = DriverManager.getConnection(oriDBUrl, username, password);
|
||||
Statement s = conn.createStatement();
|
||||
|
||||
// Collect DBMS version
|
||||
ResultSet out = s.executeQuery(VERSION_SQL);
|
||||
if (out.next()) {
|
||||
this.version.append(out.getString(1));
|
||||
}
|
||||
|
||||
// Collect DBMS parameters
|
||||
out = s.executeQuery(PARAMETERS_SQL);
|
||||
while (out.next()) {
|
||||
dbParameters.put(
|
||||
"("
|
||||
+ out.getString("FILE_NAME")
|
||||
+ ","
|
||||
+ out.getString("LAYER_NAME")
|
||||
+ ","
|
||||
+ out.getString("TENANT_NAME")
|
||||
+ ","
|
||||
+ out.getString("HOST")
|
||||
+ ","
|
||||
+ out.getString("SECTION")
|
||||
+ ","
|
||||
+ out.getString("KEY")
|
||||
+ ")",
|
||||
out.getString("VALUE"));
|
||||
}
|
||||
|
||||
// Collect DBMS internal metrics
|
||||
for (String viewName : SAP_SYS_VIEW) {
|
||||
out = s.executeQuery("SELECT * FROM " + viewName);
|
||||
pgMetrics.put(viewName, getMetrics(out));
|
||||
}
|
||||
for (String viewName : SAP_SYS_LOCAL_VIEW) {
|
||||
out = s.executeQuery("SELECT * FROM " + viewName + " where schema_name = 'SYSTEM' ");
|
||||
pgMetrics.put(viewName, getMetrics(out));
|
||||
}
|
||||
conn.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Error while collecting DB parameters: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMetrics() {
|
||||
return (pgMetrics.isEmpty() == false);
|
||||
}
|
||||
|
||||
private JSONObject genMapJSONObj(Map<String, String> mapin) {
|
||||
JSONObject res = new JSONObject();
|
||||
try {
|
||||
for (String key : mapin.keySet()) {
|
||||
res.put(key, mapin.get(key));
|
||||
}
|
||||
} catch (JSONException je) {
|
||||
LOG.error(je);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private JSONObject genLocalJSONObj(String viewName, String jsonKeyName) {
|
||||
JSONObject thisViewObj = new JSONObject();
|
||||
List<Map<String, String>> thisViewList = pgMetrics.get(viewName);
|
||||
try {
|
||||
for (Map<String, String> dbmap : thisViewList) {
|
||||
String jsonkey = dbmap.get(jsonKeyName);
|
||||
thisViewObj.put(jsonkey, genMapJSONObj(dbmap));
|
||||
}
|
||||
} catch (JSONException je) {
|
||||
LOG.error(je);
|
||||
}
|
||||
return thisViewObj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectMetrics() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobGlobal = new JSONObject();
|
||||
|
||||
JSONObject jobMetric = new JSONObject();
|
||||
for (int i = 0; i < SAP_SYS_VIEW_GLOBAL.length; i++) {
|
||||
String viewName = SAP_SYS_VIEW_GLOBAL[i];
|
||||
String jsonKeyName = SAP_SYS_VIEW_GLOBAL_KEY[i];
|
||||
jobGlobal.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
// add global json object
|
||||
stringer.value(jobGlobal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
|
||||
// create local objects for the rest of the views
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
|
||||
// "table"
|
||||
JSONObject jobTable = new JSONObject();
|
||||
for (int i = 0; i < SAP_SYS_VIEW_LOCAL_TABLE.length; i++) {
|
||||
String viewName = SAP_SYS_VIEW_LOCAL_TABLE[i];
|
||||
String jsonKeyName = SAP_SYS_VIEW_LOCAL_TABLE_KEY[i];
|
||||
jobTable.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
jobLocal.put("table", jobTable);
|
||||
|
||||
// "indexes"
|
||||
JSONObject jobIndexes = new JSONObject();
|
||||
for (int i = 0; i < SAP_SYS_VIEW_LOCAL_INDEXES.length; i++) {
|
||||
String viewName = SAP_SYS_VIEW_LOCAL_INDEXES[i];
|
||||
String jsonKeyName = SAP_SYS_VIEW_LOCAL_INDEXES_KEY[i];
|
||||
jobIndexes.put(viewName, genLocalJSONObj(viewName, jsonKeyName));
|
||||
}
|
||||
jobLocal.put("indexes", jobIndexes);
|
||||
|
||||
// add local json object
|
||||
stringer.value(jobLocal);
|
||||
stringer.endObject();
|
||||
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
|
||||
private static List<Map<String, String>> getMetrics(ResultSet out) throws SQLException {
|
||||
ResultSetMetaData metadata = out.getMetaData();
|
||||
int numColumns = metadata.getColumnCount();
|
||||
String[] columnNames = new String[numColumns];
|
||||
for (int i = 0; i < numColumns; ++i) {
|
||||
columnNames[i] = metadata.getColumnName(i + 1).toLowerCase();
|
||||
}
|
||||
|
||||
List<Map<String, String>> metrics = new ArrayList<Map<String, String>>();
|
||||
while (out.next()) {
|
||||
Map<String, String> metricMap = new TreeMap<String, String>();
|
||||
for (int i = 0; i < numColumns; ++i) {
|
||||
metricMap.put(columnNames[i], out.getString(i + 1));
|
||||
}
|
||||
metrics.add(metricMap);
|
||||
}
|
||||
return metrics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String collectParameters() {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
stringer.object();
|
||||
stringer.key(JSON_GLOBAL_KEY);
|
||||
JSONObject jobLocal = new JSONObject();
|
||||
JSONObject job = new JSONObject();
|
||||
for (String k : dbParameters.keySet()) {
|
||||
job.put(k, dbParameters.get(k));
|
||||
}
|
||||
// "global is a fake view_name (a placeholder)"
|
||||
jobLocal.put("global", job);
|
||||
stringer.value(jobLocal);
|
||||
stringer.key(JSON_LOCAL_KEY);
|
||||
stringer.value(null);
|
||||
stringer.endObject();
|
||||
} catch (JSONException jsonexn) {
|
||||
jsonexn.printStackTrace();
|
||||
}
|
||||
return JSONUtil.format(stringer.toString());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "summary of collector output",
|
||||
"description": "config files: user input",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database_type": {
|
||||
"type" : "string"
|
||||
},
|
||||
"database_url": {
|
||||
"type" : "string"
|
||||
},
|
||||
"username" :{
|
||||
"type" : "string"
|
||||
},
|
||||
"password": {
|
||||
"type" : "string"
|
||||
},
|
||||
"upload_code": {
|
||||
"type" : "string"
|
||||
},
|
||||
"upload_url": {
|
||||
"type" : "string"
|
||||
},
|
||||
"workload_name": {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"database_type",
|
||||
"database_url",
|
||||
"username",
|
||||
"password",
|
||||
"upload_code",
|
||||
"upload_url",
|
||||
"workload_name"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "collector output",
|
||||
"description": "Collected metrics or knobs info",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"global" : {
|
||||
"type": ["object", "null"],
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "object",
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"local" : {
|
||||
"type": ["object", "null"],
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "object",
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "object",
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "object",
|
||||
"patternProperties" : {
|
||||
"^[A-Za-z0-9 -_]" : {
|
||||
"type" : "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": ["global", "local"]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||
"title": "summary of collector output",
|
||||
"description": "output summary",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"database_type": {
|
||||
"type" : "string"
|
||||
},
|
||||
"start_time": {
|
||||
"type" : "number"
|
||||
},
|
||||
"observation_time" :{
|
||||
"type" : "number"
|
||||
},
|
||||
"end_time": {
|
||||
"type" : "number"
|
||||
},
|
||||
"database_version": {
|
||||
"type" : "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"database_type",
|
||||
"start_time",
|
||||
"observation_time",
|
||||
"end_time",
|
||||
"database_version"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* OtterTune - DatabaseType.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/** Database Type. */
|
||||
public enum DatabaseType {
|
||||
|
||||
/** Parameters: (1) JDBC Driver String */
|
||||
MYSQL("com.mysql.jdbc.Driver"),
|
||||
MYROCKS("com.mysql.jdbc.Driver"),
|
||||
POSTGRES("org.postgresql.Driver"),
|
||||
SAPHANA("com.sap.db.jdbc.Driver"),
|
||||
ORACLE("oracle.jdbc.driver.OracleDriver");
|
||||
|
||||
private DatabaseType(String driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the suggested driver string to use in the configuration xml This corresponds to the
|
||||
* <B>'driver'</b> attribute.
|
||||
*/
|
||||
private final String driver;
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// ACCESSORS
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Returns the suggested driver string to use for the given database type
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getSuggestedDriver() {
|
||||
return (this.driver);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// STATIC METHODS + MEMBERS
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
protected static final Map<Integer, DatabaseType> idx_lookup =
|
||||
new HashMap<Integer, DatabaseType>();
|
||||
protected static final Map<String, DatabaseType> name_lookup =
|
||||
new HashMap<String, DatabaseType>();
|
||||
|
||||
static {
|
||||
for (DatabaseType vt : EnumSet.allOf(DatabaseType.class)) {
|
||||
DatabaseType.idx_lookup.put(vt.ordinal(), vt);
|
||||
DatabaseType.name_lookup.put(vt.name().toUpperCase(), vt);
|
||||
}
|
||||
}
|
||||
|
||||
public static DatabaseType get(String name) {
|
||||
DatabaseType ret = DatabaseType.name_lookup.get(name.toUpperCase());
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* OtterTune - JSONSchemaType.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.types;
|
||||
|
||||
import com.controller.util.FileUtil;
|
||||
import com.controller.util.ValidationUtils;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
|
||||
import com.github.fge.jsonschema.main.JsonSchema;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public enum JSONSchemaType {
|
||||
|
||||
/** Parameters: (1) schema filename */
|
||||
OUTPUT("schema.json"),
|
||||
CONFIG("config_schema.json"),
|
||||
SUMMARY("summary_schema.json");
|
||||
|
||||
// Path to JSON schema directory
|
||||
private static final String SCHEMA_PATH = "src/main/java/com/controller/json_validation_schema";
|
||||
|
||||
private final JsonSchema schema;
|
||||
|
||||
private JSONSchemaType(String fileName) {
|
||||
JsonSchema newSchema = null;
|
||||
String configPath = FileUtil.joinPath(SCHEMA_PATH, fileName);
|
||||
try {
|
||||
newSchema = ValidationUtils.getSchemaNode(new File(configPath));
|
||||
} catch (IOException | ProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
this.schema = newSchema;
|
||||
}
|
||||
|
||||
public JsonSchema getSchema() {
|
||||
return this.schema;
|
||||
}
|
||||
|
||||
public static boolean isValidJson(JSONSchemaType schemaType, String jsonString) {
|
||||
try {
|
||||
JsonNode jsonNode = ValidationUtils.getJsonNode(jsonString);
|
||||
return ValidationUtils.isJsonValid(schemaType.getSchema(), jsonNode);
|
||||
} catch (IOException | ProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean isValidJson(JSONSchemaType schemaType, File jsonFile) {
|
||||
try {
|
||||
JsonNode jsonNode = ValidationUtils.getJsonNode(jsonFile);
|
||||
return ValidationUtils.isJsonValid(schemaType.getSchema(), jsonNode);
|
||||
} catch (IOException | ProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* OtterTune - ClassUtil.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.apache.commons.collections15.CollectionUtils;
|
||||
import org.apache.commons.lang.ClassUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/** @author pavlo */
|
||||
public abstract class ClassUtil {
|
||||
private static final Logger LOG = Logger.getLogger(ClassUtil.class);
|
||||
|
||||
private static final Class<?>[] EMPTY_ARRAY = new Class[] {};
|
||||
|
||||
private static final Map<Class<?>, List<Class<?>>> CACHE_getSuperClasses =
|
||||
new HashMap<Class<?>, List<Class<?>>>();
|
||||
private static final Map<Class<?>, Set<Class<?>>> CACHE_getInterfaceClasses =
|
||||
new HashMap<Class<?>, Set<Class<?>>>();
|
||||
|
||||
/**
|
||||
* Check if the given object is an array (primitve or native).
|
||||
* http://www.java2s.com/Code/Java/Reflection/Checkifthegivenobjectisanarrayprimitveornative.htm
|
||||
*
|
||||
* @param obj Object to test.
|
||||
* @return True of the object is an array.
|
||||
*/
|
||||
public static boolean isArray(final Object obj) {
|
||||
return (obj != null ? obj.getClass().isArray() : false);
|
||||
}
|
||||
|
||||
public static boolean[] isArray(final Object[] objs) {
|
||||
boolean[] isArray = new boolean[objs.length];
|
||||
for (int i = 0; i < objs.length; i++) {
|
||||
isArray[i] = ClassUtil.isArray(objs[i]);
|
||||
} // FOR
|
||||
return (isArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a Enum array to a Field array This assumes that the name of each Enum element
|
||||
* corresponds to a data member in the clas
|
||||
*
|
||||
* @param <E>
|
||||
* @param clazz
|
||||
* @param members
|
||||
* @return
|
||||
* @throws NoSuchFieldException
|
||||
*/
|
||||
public static <E extends Enum<?>> Field[] getFieldsFromMembersEnum(Class<?> clazz, E[] members)
|
||||
throws NoSuchFieldException {
|
||||
Field[] fields = new Field[members.length];
|
||||
for (int i = 0; i < members.length; i++) {
|
||||
fields[i] = clazz.getDeclaredField(members[i].name().toLowerCase());
|
||||
} // FOR
|
||||
return (fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the generic types for the given field
|
||||
*
|
||||
* @param field
|
||||
* @return
|
||||
*/
|
||||
public static List<Class<?>> getGenericTypes(Field field) {
|
||||
ArrayList<Class<?>> genericClasses = new ArrayList<Class<?>>();
|
||||
Type gtype = field.getGenericType();
|
||||
if (gtype instanceof ParameterizedType) {
|
||||
ParameterizedType ptype = (ParameterizedType) gtype;
|
||||
getGenericTypesImpl(ptype, genericClasses);
|
||||
}
|
||||
return (genericClasses);
|
||||
}
|
||||
|
||||
private static void getGenericTypesImpl(ParameterizedType ptype, List<Class<?>> classes) {
|
||||
// list the actual type arguments
|
||||
for (Type t : ptype.getActualTypeArguments()) {
|
||||
if (t instanceof Class) {
|
||||
// System.err.println("C: " + t);
|
||||
classes.add((Class<?>) t);
|
||||
} else if (t instanceof ParameterizedType) {
|
||||
ParameterizedType next = (ParameterizedType) t;
|
||||
// System.err.println("PT: " + next);
|
||||
classes.add((Class<?>) next.getRawType());
|
||||
getGenericTypesImpl(next, classes);
|
||||
}
|
||||
} // FOR
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return an ordered list of all the sub-classes for a given class Useful when dealing with
|
||||
* generics
|
||||
*
|
||||
* @param elementClass
|
||||
* @return
|
||||
*/
|
||||
public static List<Class<?>> getSuperClasses(Class<?> elementClass) {
|
||||
List<Class<?>> ret = ClassUtil.CACHE_getSuperClasses.get(elementClass);
|
||||
if (ret == null) {
|
||||
ret = new ArrayList<Class<?>>();
|
||||
while (elementClass != null) {
|
||||
ret.add(elementClass);
|
||||
elementClass = elementClass.getSuperclass();
|
||||
} // WHILE
|
||||
ret = Collections.unmodifiableList(ret);
|
||||
ClassUtil.CACHE_getSuperClasses.put(elementClass, ret);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a set of all of the interfaces that the element_class implements
|
||||
*
|
||||
* @param elementClass
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Collection<Class<?>> getInterfaces(Class<?> elementClass) {
|
||||
Set<Class<?>> ret = ClassUtil.CACHE_getInterfaceClasses.get(elementClass);
|
||||
if (ret == null) {
|
||||
// ret = new HashSet<Class<?>>();
|
||||
// Queue<Class<?>> queue = new LinkedList<Class<?>>();
|
||||
// queue.add(element_class);
|
||||
// while (!queue.isEmpty()) {
|
||||
// Class<?> current = queue.poll();
|
||||
// for (Class<?> i : current.getInterfaces()) {
|
||||
// ret.add(i);
|
||||
// queue.add(i);
|
||||
// } // FOR
|
||||
// } // WHILE
|
||||
ret = new HashSet<Class<?>>(ClassUtils.getAllInterfaces(elementClass));
|
||||
if (elementClass.isInterface()) {
|
||||
ret.add(elementClass);
|
||||
}
|
||||
ret = Collections.unmodifiableSet(ret);
|
||||
ClassUtil.CACHE_getInterfaceClasses.put(elementClass, ret);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> T newInstance(String className, Object[] params, Class<?>[] classes) {
|
||||
return ((T) ClassUtil.newInstance(ClassUtil.getClass(className), params, classes));
|
||||
}
|
||||
|
||||
public static <T> T newInstance(Class<T> targetClass, Object[] params, Class<?>[] classes) {
|
||||
// Class<?> const_params[] = new Class<?>[params.length];
|
||||
// for (int i = 0; i < params.length; i++) {
|
||||
// const_params[i] = params[i].getClass();
|
||||
// System.err.println("[" + i + "] " + params[i] + " " + params[i].getClass());
|
||||
// } // FOR
|
||||
|
||||
Constructor<T> constructor = ClassUtil.getConstructor(targetClass, classes);
|
||||
T ret = null;
|
||||
try {
|
||||
ret = constructor.newInstance(params);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(
|
||||
"Failed to create new instance of " + targetClass.getSimpleName(), ex);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an object for the given class and initialize it from conf
|
||||
*
|
||||
* @param theClass class of which an object is created
|
||||
* @param expected the expected parent class or interface
|
||||
* @return a new object
|
||||
*/
|
||||
public static <T> T newInstance(Class<?> theClass, Class<T> expected) {
|
||||
T result;
|
||||
try {
|
||||
if (!expected.isAssignableFrom(theClass)) {
|
||||
throw new Exception(
|
||||
"Specified class "
|
||||
+ theClass.getName()
|
||||
+ ""
|
||||
+ "does not extend/implement "
|
||||
+ expected.getName());
|
||||
}
|
||||
Class<? extends T> clazz = (Class<? extends T>) theClass;
|
||||
Constructor<? extends T> meth = clazz.getDeclaredConstructor(EMPTY_ARRAY);
|
||||
meth.setAccessible(true);
|
||||
result = meth.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static <T> T newInstance(String className, Class<T> expected)
|
||||
throws ClassNotFoundException {
|
||||
return newInstance(getClass(className), expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @param targetClass
|
||||
* @param params
|
||||
* @return
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Constructor<T> getConstructor(Class<T> targetClass, Class<?>... params) {
|
||||
NoSuchMethodException error = null;
|
||||
try {
|
||||
return (targetClass.getConstructor(params));
|
||||
} catch (NoSuchMethodException ex) {
|
||||
// The first time we get this it can be ignored
|
||||
// We'll try to be nice and find a match for them
|
||||
error = ex;
|
||||
}
|
||||
assert (error != null);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("TARGET CLASS: " + targetClass);
|
||||
LOG.debug("TARGET PARAMS: " + Arrays.toString(params));
|
||||
}
|
||||
|
||||
List<Class<?>>[] paramSuper = (List<Class<?>>[]) new List[params.length];
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
paramSuper[i] = ClassUtil.getSuperClasses(params[i]);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(" SUPER[" + params[i].getSimpleName() + "] => " + paramSuper[i]);
|
||||
}
|
||||
} // FOR
|
||||
|
||||
for (Constructor<?> c : targetClass.getConstructors()) {
|
||||
Class<?>[] ctypes = c.getParameterTypes();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("CANDIDATE: " + c);
|
||||
LOG.debug("CANDIDATE PARAMS: " + Arrays.toString(ctypes));
|
||||
}
|
||||
if (params.length != ctypes.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
List<Class<?>> csuper = ClassUtil.getSuperClasses(ctypes[i]);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(" SUPER[" + ctypes[i].getSimpleName() + "] => " + csuper);
|
||||
}
|
||||
if (CollectionUtils.intersection(paramSuper[i], csuper).isEmpty() == false) {
|
||||
return ((Constructor<T>) c);
|
||||
}
|
||||
} // FOR (param)
|
||||
} // FOR (constructors)
|
||||
throw new RuntimeException(
|
||||
"Failed to retrieve constructor for " + targetClass.getSimpleName(), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param className
|
||||
* @return
|
||||
*/
|
||||
public static Class<?> getClass(String className) {
|
||||
Class<?> targetClass = null;
|
||||
try {
|
||||
ClassLoader loader = ClassLoader.getSystemClassLoader();
|
||||
targetClass = (Class<?>) loader.loadClass(className);
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to retrieve class for " + className, ex);
|
||||
}
|
||||
return (targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if asserts are enabled. This assumes that we're always using the default system
|
||||
* ClassLoader
|
||||
*/
|
||||
public static boolean isAssertsEnabled() {
|
||||
boolean ret = false;
|
||||
try {
|
||||
assert (false);
|
||||
} catch (AssertionError ex) {
|
||||
ret = true;
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* OtterTune - CollectionUtil.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import java.util.AbstractList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import org.apache.commons.collections15.set.ListOrderedSet;
|
||||
import org.apache.commons.lang.NotImplementedException;
|
||||
|
||||
/** @author pavlo */
|
||||
public abstract class CollectionUtil {
|
||||
|
||||
private static final Random RANDOM = new Random();
|
||||
|
||||
/**
|
||||
* Put all of the elements in items into the given array This assumes that the array has been
|
||||
* pre-allocated
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @param array
|
||||
*/
|
||||
public static <T> void toArray(
|
||||
Collection<T> items, Object[] array, boolean convertToPrimitive) {
|
||||
assert (items.size() == array.length);
|
||||
|
||||
int i = 0;
|
||||
for (T t : items) {
|
||||
if (convertToPrimitive) {
|
||||
if (t instanceof Long) {
|
||||
array[i] = ((Long) t).longValue();
|
||||
} else if (t instanceof Integer) {
|
||||
array[i] = ((Integer) t).intValue();
|
||||
} else if (t instanceof Double) {
|
||||
array[i] = ((Double) t).doubleValue();
|
||||
} else if (t instanceof Boolean) {
|
||||
array[i] = ((Boolean) t).booleanValue();
|
||||
}
|
||||
} else {
|
||||
array[i] = t;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public static int[] toIntArray(Collection<Integer> items) {
|
||||
int[] ret = new int[items.size()];
|
||||
int idx = 0;
|
||||
for (Integer i : items) {
|
||||
assert (i != null);
|
||||
ret[idx++] = i.intValue();
|
||||
} // FOR
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all the values of an Iterator into a List
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> list(Iterator<T> it) {
|
||||
List<T> list = new ArrayList<T>();
|
||||
CollectionUtil.addAll(list, it);
|
||||
return (list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all of the values of an Enumeration into a new List
|
||||
*
|
||||
* @param <T>
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> list(Enumeration<T> e) {
|
||||
return (list(iterable(e)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all of the values of an Iterable into a new List
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<T> list(Iterable<T> it) {
|
||||
return (list(it.iterator()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all the values of an Iterator into a Set
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> Set<T> set(Iterator<T> it) {
|
||||
Set<T> set = new HashSet<T>();
|
||||
CollectionUtil.addAll(set, it);
|
||||
return (set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put all of the values of an Iterable into a new Set
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> Set<T> set(Iterable<T> it) {
|
||||
return (set(it.iterator()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list containing the string representations of the elements in the collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static <T> List<String> toStringList(Collection<T> data) {
|
||||
List<String> ret = new ArrayList<String>();
|
||||
for (T t : data) {
|
||||
ret.add(t.toString());
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set containing the string representations of the elements in the collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @return
|
||||
*/
|
||||
public static <T> Set<String> toStringSet(Collection<T> data) {
|
||||
Set<String> ret = new HashSet<String>();
|
||||
for (T t : data) {
|
||||
ret.add(t.toString());
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random value from the given Collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
*/
|
||||
public static <T> T random(Collection<T> items) {
|
||||
return (CollectionUtil.random(items, RANDOM));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random value from the given Collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @param rand
|
||||
* @return
|
||||
*/
|
||||
public static <T> T random(Collection<T> items, Random rand) {
|
||||
int idx = rand.nextInt(items.size());
|
||||
return (CollectionUtil.get(items, idx));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random value from the given Iterable
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> T random(Iterable<T> it) {
|
||||
return (CollectionUtil.random(it, RANDOM));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a random value from the given Iterable
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @param rand
|
||||
* @return
|
||||
*/
|
||||
public static <T> T random(Iterable<T> it, Random rand) {
|
||||
List<T> list = new ArrayList<T>();
|
||||
for (T t : it) {
|
||||
list.add(t);
|
||||
} // FOR
|
||||
return (CollectionUtil.random(list, rand));
|
||||
}
|
||||
|
||||
public static <E extends Enum<?>> Set<E> getAllExcluding(E[] elements, E... excluding) {
|
||||
Set<E> excludeSet = new HashSet<E>();
|
||||
for (E e : excluding) {
|
||||
excludeSet.add(e);
|
||||
}
|
||||
|
||||
Set<E> elementsSet = new HashSet<E>();
|
||||
for (int i = 0; i < elements.length; i++) {
|
||||
if (!excludeSet.contains(elements[i])) {
|
||||
elementsSet.add(elements[i]);
|
||||
}
|
||||
} // FOR
|
||||
return (elementsSet);
|
||||
// Crappy java....
|
||||
// Object new_elements[] = new Object[elements_set.size()];
|
||||
// elements_set.toArray(new_elements);
|
||||
// return ((E[])new_elements);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the items in the array to a Collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @param items
|
||||
*/
|
||||
public static <T> Collection<T> addAll(Collection<T> data, T... items) {
|
||||
for (T i : (T[]) items) {
|
||||
data.add(i);
|
||||
}
|
||||
return (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all the items in the Enumeration into a Collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @param items
|
||||
*/
|
||||
public static <T> Collection<T> addAll(Collection<T> data, Enumeration<T> items) {
|
||||
while (items.hasMoreElements()) {
|
||||
data.add(items.nextElement());
|
||||
} // WHILE
|
||||
return (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all of the items from the Iterable into the given collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @param items
|
||||
*/
|
||||
public static <T> Collection<T> addAll(Collection<T> data, Iterable<T> items) {
|
||||
return (CollectionUtil.addAll(data, items.iterator()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all of the items from the Iterator into the given collection
|
||||
*
|
||||
* @param <T>
|
||||
* @param data
|
||||
* @param items
|
||||
*/
|
||||
public static <T> Collection<T> addAll(Collection<T> data, Iterator<T> items) {
|
||||
while (items.hasNext()) {
|
||||
data.add(items.next());
|
||||
} // WHILE
|
||||
return (data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @param <U>
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
public static <T, U extends Comparable<U>> T getGreatest(Map<T, U> map) {
|
||||
T maxKey = null;
|
||||
U maxValue = null;
|
||||
for (Entry<T, U> e : map.entrySet()) {
|
||||
T key = e.getKey();
|
||||
U value = e.getValue();
|
||||
if (maxValue == null || value.compareTo(maxValue) > 0) {
|
||||
maxValue = value;
|
||||
maxKey = key;
|
||||
}
|
||||
} // FOR
|
||||
return (maxKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first item in a Iterable
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
public static <T> T first(Iterable<T> items) {
|
||||
return (CollectionUtil.get(items, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first item in a Iterator
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
public static <T> T first(Iterator<T> items) {
|
||||
return (items.hasNext() ? items.next() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first item in an Enumeration
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
public static <T> T first(Enumeration<T> items) {
|
||||
return (items.hasMoreElements() ? items.nextElement() : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ith element of a set. Super lame
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @param idx
|
||||
* @return
|
||||
*/
|
||||
public static <T> T get(Iterable<T> items, int idx) {
|
||||
if (items instanceof AbstractList<?>) {
|
||||
return ((AbstractList<T>) items).get(idx);
|
||||
} else if (items instanceof ListOrderedSet<?>) {
|
||||
return ((ListOrderedSet<T>) items).get(idx);
|
||||
}
|
||||
int ctr = 0;
|
||||
for (T t : items) {
|
||||
if (ctr++ == idx) {
|
||||
return (t);
|
||||
}
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last item in an Iterable
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
public static <T> T last(Iterable<T> items) {
|
||||
T last = null;
|
||||
if (items instanceof AbstractList<?>) {
|
||||
AbstractList<T> list = (AbstractList<T>) items;
|
||||
last = (list.isEmpty() ? null : list.get(list.size() - 1));
|
||||
} else {
|
||||
for (T t : items) {
|
||||
last = t;
|
||||
}
|
||||
}
|
||||
return (last);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last item in an array
|
||||
*
|
||||
* @param <T>
|
||||
* @param items
|
||||
* @return
|
||||
*/
|
||||
public static <T> T last(T... items) {
|
||||
if (items != null && items.length > 0) {
|
||||
return (items[items.length - 1]);
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
* @param map
|
||||
* @return
|
||||
*/
|
||||
public static <K extends Comparable<?>, V> List<V> getSortedList(
|
||||
SortedMap<K, Collection<V>> map) {
|
||||
List<V> ret = new ArrayList<V>();
|
||||
for (Collection<V> col : map.values()) {
|
||||
ret.addAll(col);
|
||||
} // FOR
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an Iterable around an Iterator
|
||||
*
|
||||
* @param <T>
|
||||
* @param it
|
||||
* @return
|
||||
*/
|
||||
public static <T> Iterable<T> iterable(final Iterator<T> it) {
|
||||
return (new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return (it);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> Iterable<T> iterable(final T[] values) {
|
||||
return (new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
private int idx = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (this.idx < values.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
if (this.idx == values.length) {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
return values[this.idx++];
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap an Iterable around an Enumeration
|
||||
*
|
||||
* @param <T>
|
||||
* @param e
|
||||
* @return
|
||||
*/
|
||||
public static <T> Iterable<T> iterable(final Enumeration<T> e) {
|
||||
return (new Iterable<T>() {
|
||||
@Override
|
||||
public Iterator<T> iterator() {
|
||||
return new Iterator<T>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (e.hasMoreElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next() {
|
||||
return (e.nextElement());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static <T> T pop(Collection<T> items) {
|
||||
T t = CollectionUtil.first(items);
|
||||
if (t != null) {
|
||||
boolean ret = items.remove(t);
|
||||
assert (ret);
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* OtterTune - FileUtil.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/** @author pavlo */
|
||||
public abstract class FileUtil {
|
||||
private static final Logger LOG = Logger.getLogger(FileUtil.class);
|
||||
|
||||
private static final Pattern EXT_SPLIT = Pattern.compile("\\.");
|
||||
|
||||
/**
|
||||
* Join path components
|
||||
*
|
||||
* @param args
|
||||
* @return
|
||||
*/
|
||||
public static String joinPath(String... args) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (String a : args) {
|
||||
if (a != null && a.length() > 0) {
|
||||
if (!first) {
|
||||
result.append("/");
|
||||
}
|
||||
result.append(a);
|
||||
first = false;
|
||||
}
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a basename for a file, find the next possible filename if this file already exists. For
|
||||
* example, if the file test.res already exists, create a file called, test.1.res
|
||||
*
|
||||
* @param basename
|
||||
* @return
|
||||
*/
|
||||
public static String getNextFilename(String basename) {
|
||||
|
||||
if (!exists(basename)) {
|
||||
return basename;
|
||||
}
|
||||
|
||||
File f = new File(basename);
|
||||
if (f != null && f.isFile()) {
|
||||
String[] parts = EXT_SPLIT.split(basename);
|
||||
|
||||
// Check how many files already exist
|
||||
int counter = 1;
|
||||
String nextName = parts[0] + "." + counter + "." + parts[1];
|
||||
while (exists(nextName)) {
|
||||
++counter;
|
||||
nextName = parts[0] + "." + counter + "." + parts[1];
|
||||
}
|
||||
return nextName;
|
||||
}
|
||||
|
||||
// Should we throw instead??
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean exists(String path) {
|
||||
return (new File(path).exists());
|
||||
}
|
||||
|
||||
public static String realpath(String path) {
|
||||
File f = new File(path);
|
||||
String ret = null;
|
||||
try {
|
||||
ret = f.getCanonicalPath();
|
||||
} catch (Exception ex) {
|
||||
LOG.warn(ex);
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
public static String basename(String path) {
|
||||
return (new File(path)).getName();
|
||||
}
|
||||
|
||||
public static String getExtension(File f) {
|
||||
if (f != null && f.isFile()) {
|
||||
String[] parts = EXT_SPLIT.split(f.getName());
|
||||
if (parts.length > 1) {
|
||||
return (parts[parts.length - 1]);
|
||||
}
|
||||
}
|
||||
return (null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create any directory in the list paths if it doesn't exist
|
||||
*
|
||||
* @param paths
|
||||
*/
|
||||
public static void makeDirIfNotExists(String... paths) {
|
||||
for (String p : paths) {
|
||||
if (p == null) {
|
||||
continue;
|
||||
}
|
||||
File f = new File(p);
|
||||
if (f.exists() == false) {
|
||||
f.mkdirs();
|
||||
}
|
||||
} // FOR
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a File handle to a temporary file location
|
||||
*
|
||||
* @param ext the suffix of the filename
|
||||
* @param deleteOnExit whether to delete this file after the JVM exits
|
||||
* @return
|
||||
*/
|
||||
public static File getTempFile(String ext, boolean deleteOnExit) {
|
||||
return getTempFile(null, ext, deleteOnExit);
|
||||
}
|
||||
|
||||
public static File getTempFile(String ext) {
|
||||
return (FileUtil.getTempFile(null, ext, false));
|
||||
}
|
||||
|
||||
public static File getTempFile(String prefix, String suffix, boolean deleteOnExit) {
|
||||
File tempFile;
|
||||
if (suffix != null && suffix.startsWith(".") == false) {
|
||||
suffix = "." + suffix;
|
||||
}
|
||||
if (prefix == null) {
|
||||
prefix = "hstore";
|
||||
}
|
||||
|
||||
try {
|
||||
tempFile = File.createTempFile(prefix, suffix);
|
||||
if (deleteOnExit) {
|
||||
tempFile.deleteOnExit();
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return (tempFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsafely create a temporary directory Yes I said that this was unsafe. I don't care...
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static File getTempDirectory() {
|
||||
final File temp = FileUtil.getTempFile(null);
|
||||
if (!(temp.delete())) {
|
||||
throw new RuntimeException("Could not delete temp file: " + temp.getAbsolutePath());
|
||||
} else if (!(temp.mkdir())) {
|
||||
throw new RuntimeException("Could not create temp directory: " + temp.getAbsolutePath());
|
||||
}
|
||||
return (temp);
|
||||
}
|
||||
|
||||
public static File writeStringToFile(String filePath, String content) throws IOException {
|
||||
return (FileUtil.writeStringToFile(new File(filePath), content));
|
||||
}
|
||||
|
||||
public static File writeStringToFile(File file, String content) throws IOException {
|
||||
FileWriter writer = new FileWriter(file);
|
||||
writer.write(content);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
return (file);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given string to a temporary file Will not delete the file after the JVM exits
|
||||
*
|
||||
* @param content
|
||||
* @return
|
||||
*/
|
||||
public static File writeStringToTempFile(String content) {
|
||||
return (writeStringToTempFile(content, "tmp", false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given string to a temporary file with the given extension as the suffix Will not
|
||||
* delete the file after the JVM exits
|
||||
*
|
||||
* @param content
|
||||
* @param ext
|
||||
* @return
|
||||
*/
|
||||
public static File writeStringToTempFile(String content, String ext) {
|
||||
return (writeStringToTempFile(content, ext, false));
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the given string to a temporary file with the given extension as the suffix If
|
||||
* deleteOnExit is true, then the file will be removed when the JVM exits
|
||||
*
|
||||
* @param content
|
||||
* @param ext
|
||||
* @param deleteOnExit
|
||||
* @return
|
||||
*/
|
||||
public static File writeStringToTempFile(String content, String ext, boolean deleteOnExit) {
|
||||
File tempFile = FileUtil.getTempFile(ext, deleteOnExit);
|
||||
try {
|
||||
FileUtil.writeStringToFile(tempFile, content);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
public static String readFile(File path) {
|
||||
LOG.debug(path);
|
||||
return (readFile(path.getAbsolutePath()));
|
||||
}
|
||||
|
||||
public static String readFile(String path) {
|
||||
StringBuilder buffer = new StringBuilder();
|
||||
try {
|
||||
BufferedReader in = FileUtil.getReader(path);
|
||||
while (in.ready()) {
|
||||
buffer.append(in.readLine()).append("\n");
|
||||
} // WHILE
|
||||
in.close();
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException("Failed to read file contents from '" + path + "'", ex);
|
||||
}
|
||||
return (buffer.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BufferedReader for the given input path Can handle both gzip and plain text files
|
||||
*
|
||||
* @param path
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BufferedReader getReader(String path) throws IOException {
|
||||
return (FileUtil.getReader(new File(path)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BufferedReader for the given input path Can handle both gzip and plain text files
|
||||
*
|
||||
* @param file
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BufferedReader getReader(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
throw new IOException("The file '" + file + "' does not exist");
|
||||
}
|
||||
|
||||
BufferedReader in = null;
|
||||
if (file.getPath().endsWith(".gz")) {
|
||||
FileInputStream fin = new FileInputStream(file);
|
||||
GZIPInputStream gzis = new GZIPInputStream(fin);
|
||||
in = new BufferedReader(new InputStreamReader(gzis));
|
||||
LOG.debug("Reading in the zipped contents of '" + file.getName() + "'");
|
||||
} else {
|
||||
in = new BufferedReader(new FileReader(file));
|
||||
LOG.debug("Reading in the contents of '" + file.getName() + "'");
|
||||
}
|
||||
return (in);
|
||||
}
|
||||
|
||||
public static byte[] readBytesFromFile(String path) throws IOException {
|
||||
File file = new File(path);
|
||||
FileInputStream in = new FileInputStream(file);
|
||||
|
||||
// Create the byte array to hold the data
|
||||
long length = file.length();
|
||||
byte[] bytes = new byte[(int) length];
|
||||
|
||||
LOG.debug("Reading in the contents of '" + file.getAbsolutePath() + "'");
|
||||
|
||||
// Read in the bytes
|
||||
int offset = 0;
|
||||
int numRead = 0;
|
||||
while ((offset < bytes.length)
|
||||
&& ((numRead = in.read(bytes, offset, bytes.length - offset)) >= 0)) {
|
||||
offset += numRead;
|
||||
} // WHILE
|
||||
if (offset < bytes.length) {
|
||||
throw new IOException("Failed to read the entire contents of '" + file.getName() + "'");
|
||||
}
|
||||
in.close();
|
||||
return (bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the path to a directory below our current location in the source tree Throws a
|
||||
* RuntimeException if we go beyond our repository checkout
|
||||
*
|
||||
* @param dirName
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static File findDirectory(String dirName) throws IOException {
|
||||
return (FileUtil.find(dirName, new File(".").getCanonicalFile(), true).getCanonicalFile());
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the path to a directory below our current location in the source tree Throws a
|
||||
* RuntimeException if we go beyond our repository checkout
|
||||
*
|
||||
* @param dirName
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static File findFile(String fileName) throws IOException {
|
||||
return (FileUtil.find(fileName, new File(".").getCanonicalFile(), false).getCanonicalFile());
|
||||
}
|
||||
|
||||
private static final File find(String name, File current, boolean isdir) throws IOException {
|
||||
LOG.debug("Find Current Location = " + current);
|
||||
boolean hasSvn = false;
|
||||
for (File file : current.listFiles()) {
|
||||
if (file.getCanonicalPath().endsWith(File.separator + name) && file.isDirectory() == isdir) {
|
||||
return (file);
|
||||
// Make sure that we don't go to far down...
|
||||
} else if (file.getCanonicalPath().endsWith(File.separator + ".svn")) {
|
||||
hasSvn = true;
|
||||
}
|
||||
} // FOR
|
||||
// If we didn't see an .svn directory, then we went too far down
|
||||
if (!hasSvn) {
|
||||
throw new RuntimeException(
|
||||
"Unable to find directory '" + name + "' [last_dir=" + current.getAbsolutePath() + "]");
|
||||
}
|
||||
File next = new File(current.getCanonicalPath() + File.separator + "");
|
||||
return (FileUtil.find(name, next, isdir));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all the files in a directory whose name starts with the provided prefix
|
||||
*
|
||||
* @param dir
|
||||
* @param filePrefix
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static List<File> getFilesInDirectory(final File dir, final String filePrefix)
|
||||
throws IOException {
|
||||
assert (dir.isDirectory()) : "Invalid search directory path: " + dir;
|
||||
FilenameFilter filter =
|
||||
new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return (name.startsWith(filePrefix));
|
||||
}
|
||||
};
|
||||
return (Arrays.asList(dir.listFiles(filter)));
|
||||
}
|
||||
|
||||
public static String getAbsPath(String dirPath) {
|
||||
String current = null;
|
||||
try {
|
||||
current = new java.io.File(dirPath).getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* OtterTune - JSONSerializable.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONString;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.io.IOException;
|
||||
|
||||
public interface JSONSerializable extends JSONString {
|
||||
public void save(String outputPath) throws IOException;
|
||||
|
||||
public void load(String inputPath) throws IOException;
|
||||
|
||||
public void toJSON(JSONStringer stringer) throws JSONException;
|
||||
|
||||
public void fromJSON(JSONObject jsonObject) throws JSONException;
|
||||
}
|
||||
@@ -0,0 +1,800 @@
|
||||
/*
|
||||
* OtterTune - JSONUtil.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import com.controller.util.json.JSONArray;
|
||||
import com.controller.util.json.JSONException;
|
||||
import com.controller.util.json.JSONObject;
|
||||
import com.controller.util.json.JSONStringer;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/** @author pavlo */
|
||||
public abstract class JSONUtil {
|
||||
private static final Logger LOG = Logger.getLogger(JSONUtil.class.getName());
|
||||
|
||||
private static final String JSON_CLASS_SUFFIX = "_class";
|
||||
private static final Map<Class<?>, Field[]> SERIALIZABLE_FIELDS =
|
||||
new HashMap<Class<?>, Field[]>();
|
||||
|
||||
/**
|
||||
* @param clazz
|
||||
* @return
|
||||
*/
|
||||
public static Field[] getSerializableFields(Class<?> clazz, String... fieldsToExclude) {
|
||||
Field[] ret = SERIALIZABLE_FIELDS.get(clazz);
|
||||
if (ret == null) {
|
||||
Collection<String> exclude = CollectionUtil.addAll(new HashSet<String>(), fieldsToExclude);
|
||||
synchronized (SERIALIZABLE_FIELDS) {
|
||||
ret = SERIALIZABLE_FIELDS.get(clazz);
|
||||
if (ret == null) {
|
||||
List<Field> fields = new ArrayList<Field>();
|
||||
for (Field f : clazz.getFields()) {
|
||||
int modifiers = f.getModifiers();
|
||||
if (Modifier.isTransient(modifiers) == false
|
||||
&& Modifier.isPublic(modifiers) == true
|
||||
&& Modifier.isStatic(modifiers) == false
|
||||
&& exclude.contains(f.getName()) == false) {
|
||||
fields.add(f);
|
||||
}
|
||||
} // FOR
|
||||
ret = fields.toArray(new Field[0]);
|
||||
SERIALIZABLE_FIELDS.put(clazz, ret);
|
||||
}
|
||||
} // SYNCH
|
||||
}
|
||||
return (ret);
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Pretty Print
|
||||
*
|
||||
* @param json
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static String format(String json) {
|
||||
try {
|
||||
return (JSONUtil.format(new JSONObject(json)));
|
||||
} catch (RuntimeException ex) {
|
||||
throw ex;
|
||||
} catch (Exception ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON Pretty Print
|
||||
*
|
||||
* @param <T>
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static <T extends JSONSerializable> String format(T object) {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
if (object instanceof JSONObject) {
|
||||
return ((JSONObject) object).toString(2);
|
||||
}
|
||||
stringer.object();
|
||||
object.toJSON(stringer);
|
||||
stringer.endObject();
|
||||
} catch (JSONException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return (JSONUtil.format(stringer.toString()));
|
||||
}
|
||||
|
||||
public static String format(JSONObject o) {
|
||||
try {
|
||||
return o.toString(1);
|
||||
} catch (JSONException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param <T>
|
||||
* @param object
|
||||
* @return
|
||||
*/
|
||||
public static String toJSONString(Object object) {
|
||||
JSONStringer stringer = new JSONStringer();
|
||||
try {
|
||||
if (object instanceof JSONSerializable) {
|
||||
stringer.object();
|
||||
((JSONSerializable) object).toJSON(stringer);
|
||||
stringer.endObject();
|
||||
} else if (object != null) {
|
||||
Class<?> clazz = object.getClass();
|
||||
// stringer.key(clazz.getSimpleName());
|
||||
JSONUtil.writeFieldValue(stringer, clazz, object);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return (stringer.toString());
|
||||
}
|
||||
|
||||
public static <T extends JSONSerializable> T fromJSONString(T t, String json) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(json);
|
||||
t.fromJSON(jsonObject);
|
||||
} catch (JSONException ex) {
|
||||
throw new RuntimeException("Failed to deserialize object " + t, ex);
|
||||
}
|
||||
return (t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of a JSONSerializable object out to a file on the local disk
|
||||
*
|
||||
* @param <T>
|
||||
* @param object
|
||||
* @param outputPath
|
||||
* @throws IOException
|
||||
*/
|
||||
public static <T extends JSONSerializable> void save(T object, String outputPath)
|
||||
throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Writing out contents of "
|
||||
+ object.getClass().getSimpleName()
|
||||
+ " to '"
|
||||
+ outputPath
|
||||
+ "'");
|
||||
}
|
||||
File f = new File(outputPath);
|
||||
try {
|
||||
FileUtil.makeDirIfNotExists(f.getParent());
|
||||
String json = object.toJSONString();
|
||||
FileUtil.writeStringToFile(f, format(json));
|
||||
} catch (Exception ex) {
|
||||
LOG.error(
|
||||
"Failed to serialize the " + object.getClass().getSimpleName() + " file '" + f + "'", ex);
|
||||
throw new IOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load in a JSONSerialable stored in a file
|
||||
*
|
||||
* @param <T>
|
||||
* @param object
|
||||
* @param inputPath
|
||||
* @throws Exception
|
||||
*/
|
||||
public static <T extends JSONSerializable> void load(T object, String inputPath)
|
||||
throws IOException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Loading in serialized "
|
||||
+ object.getClass().getSimpleName()
|
||||
+ " from '"
|
||||
+ inputPath
|
||||
+ "'");
|
||||
}
|
||||
String contents = FileUtil.readFile(inputPath);
|
||||
if (contents.isEmpty()) {
|
||||
throw new IOException(
|
||||
"The " + object.getClass().getSimpleName() + " file '" + inputPath + "' is empty");
|
||||
}
|
||||
try {
|
||||
object.fromJSON(new JSONObject(contents));
|
||||
} catch (Exception ex) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.error(
|
||||
"Failed to deserialize the "
|
||||
+ object.getClass().getSimpleName()
|
||||
+ " from file '"
|
||||
+ inputPath
|
||||
+ "'",
|
||||
ex);
|
||||
}
|
||||
throw new IOException(ex);
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("The loading of the " + object.getClass().getSimpleName() + " is complete");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given Enum, write out the contents of the corresponding field to the JSONObject We assume
|
||||
* that the given object has matching fields that correspond to the Enum members, except that
|
||||
* their names are lower case.
|
||||
*
|
||||
* @param <E>
|
||||
* @param <T>
|
||||
* @param stringer
|
||||
* @param object
|
||||
* @param baseClass
|
||||
* @param members
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static <E extends Enum<?>, T> void fieldsToJSON(
|
||||
JSONStringer stringer, T object, Class<? extends T> baseClass, E[] members)
|
||||
throws JSONException {
|
||||
try {
|
||||
fieldsToJSON(
|
||||
stringer, object, baseClass, ClassUtil.getFieldsFromMembersEnum(baseClass, members));
|
||||
} catch (NoSuchFieldException ex) {
|
||||
throw new JSONException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For a given list of Fields, write out the contents of the corresponding field to the JSONObject
|
||||
* The each of the JSONObject's elements will be the upper case version of the Field's name
|
||||
*
|
||||
* @param <T>
|
||||
* @param stringer
|
||||
* @param object
|
||||
* @param baseClass
|
||||
* @param fields
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static <T> void fieldsToJSON(
|
||||
JSONStringer stringer, T object, Class<? extends T> baseClass, Field[] fields)
|
||||
throws JSONException {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Serializing out " + fields.length + " elements for " + baseClass.getSimpleName());
|
||||
}
|
||||
for (Field f : fields) {
|
||||
String jsonKey = f.getName().toUpperCase();
|
||||
stringer.key(jsonKey);
|
||||
|
||||
try {
|
||||
Class<?> fclass = f.getType();
|
||||
Object fvalue = f.get(object);
|
||||
|
||||
// Null
|
||||
if (fvalue == null) {
|
||||
writeFieldValue(stringer, fclass, fvalue);
|
||||
// Maps
|
||||
} else if (fvalue instanceof Map) {
|
||||
writeFieldValue(stringer, fclass, fvalue);
|
||||
// Everything else
|
||||
} else {
|
||||
writeFieldValue(stringer, fclass, fvalue);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
throw new JSONException(ex);
|
||||
}
|
||||
} // FOR
|
||||
}
|
||||
|
||||
/**
|
||||
* @param stringer
|
||||
* @param fieldClass
|
||||
* @param fieldValue
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static void writeFieldValue(
|
||||
JSONStringer stringer, Class<?> fieldClass, Object fieldValue) throws JSONException {
|
||||
// Null
|
||||
if (fieldValue == null) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("writeNullFieldValue(" + fieldClass + ", " + fieldValue + ")");
|
||||
}
|
||||
stringer.value(null);
|
||||
|
||||
// Collections
|
||||
} else if (ClassUtil.getInterfaces(fieldClass).contains(Collection.class)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("writeCollectionFieldValue(" + fieldClass + ", " + fieldValue + ")");
|
||||
}
|
||||
stringer.array();
|
||||
for (Object value : (Collection<?>) fieldValue) {
|
||||
if (value == null) {
|
||||
stringer.value(null);
|
||||
} else {
|
||||
writeFieldValue(stringer, value.getClass(), value);
|
||||
}
|
||||
} // FOR
|
||||
stringer.endArray();
|
||||
|
||||
// Maps
|
||||
} else if (fieldValue instanceof Map) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("writeMapFieldValue(" + fieldClass + ", " + fieldValue + ")");
|
||||
}
|
||||
stringer.object();
|
||||
for (Entry<?, ?> e : ((Map<?, ?>) fieldValue).entrySet()) {
|
||||
// We can handle null keys
|
||||
String keyValue = null;
|
||||
if (e.getKey() != null) {
|
||||
// deserialize it on the other side
|
||||
Class<?> keyClass = e.getKey().getClass();
|
||||
keyValue = makePrimitiveValue(keyClass, e.getKey()).toString();
|
||||
}
|
||||
stringer.key(keyValue);
|
||||
|
||||
// We can also handle null values. Where is your god now???
|
||||
if (e.getValue() == null) {
|
||||
stringer.value(null);
|
||||
} else {
|
||||
writeFieldValue(stringer, e.getValue().getClass(), e.getValue());
|
||||
}
|
||||
} // FOR
|
||||
stringer.endObject();
|
||||
|
||||
// Primitive
|
||||
} else {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("writePrimitiveFieldValue(" + fieldClass + ", " + fieldValue + ")");
|
||||
}
|
||||
stringer.value(makePrimitiveValue(fieldClass, fieldValue));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from the given JSONObject and populate the given Map
|
||||
*
|
||||
* @param jsonObject
|
||||
* @param map
|
||||
* @param innerClasses
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static void readMapField(
|
||||
final JSONObject jsonObject, final Map map, final Stack<Class> innerClasses)
|
||||
throws Exception {
|
||||
Class<?> keyClass = innerClasses.pop();
|
||||
Class<?> valClass = innerClasses.pop();
|
||||
Collection<Class<?>> valInterfaces = ClassUtil.getInterfaces(valClass);
|
||||
|
||||
assert (jsonObject != null);
|
||||
for (String jsonKey : CollectionUtil.iterable(jsonObject.keys())) {
|
||||
final Stack<Class> nextInnerClasses = new Stack<Class>();
|
||||
nextInnerClasses.addAll(innerClasses);
|
||||
assert (nextInnerClasses.equals(innerClasses));
|
||||
|
||||
// KEY
|
||||
Object key = JSONUtil.getPrimitiveValue(jsonKey, keyClass);
|
||||
|
||||
// VALUE
|
||||
Object object = null;
|
||||
if (jsonObject.isNull(jsonKey)) {
|
||||
// Nothing...
|
||||
} else if (valInterfaces.contains(List.class)) {
|
||||
object = new ArrayList();
|
||||
readCollectionField(
|
||||
jsonObject.getJSONArray(jsonKey), (Collection) object, nextInnerClasses);
|
||||
} else if (valInterfaces.contains(Set.class)) {
|
||||
object = new HashSet();
|
||||
readCollectionField(
|
||||
jsonObject.getJSONArray(jsonKey), (Collection) object, nextInnerClasses);
|
||||
} else if (valInterfaces.contains(Map.class)) {
|
||||
object = new HashMap();
|
||||
readMapField(jsonObject.getJSONObject(jsonKey), (Map) object, nextInnerClasses);
|
||||
} else {
|
||||
String jsonString = jsonObject.getString(jsonKey);
|
||||
try {
|
||||
object = JSONUtil.getPrimitiveValue(jsonString, valClass);
|
||||
} catch (Exception ex) {
|
||||
System.err.println("val_interfaces: " + valInterfaces);
|
||||
LOG.error(
|
||||
"Failed to deserialize value '"
|
||||
+ jsonString
|
||||
+ "' from inner map key '"
|
||||
+ jsonKey
|
||||
+ "'");
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
map.put(key, object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read data from the given JSONArray and populate the given Collection
|
||||
*
|
||||
* @param jsonArray
|
||||
* @param collection
|
||||
* @param innerClasses
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected static void readCollectionField(
|
||||
final JSONArray jsonArray, final Collection collection, final Stack<Class> innerClasses)
|
||||
throws Exception {
|
||||
// We need to figure out what the inner type of the collection is
|
||||
// If it's a Collection or a Map, then we need to instantiate it before
|
||||
// we can call readFieldValue() again for it.
|
||||
Class innerClass = innerClasses.pop();
|
||||
Collection<Class<?>> innerInterfaces = ClassUtil.getInterfaces(innerClass);
|
||||
|
||||
for (int i = 0, cnt = jsonArray.length(); i < cnt; i++) {
|
||||
final Stack<Class> nextInnerClasses = new Stack<Class>();
|
||||
nextInnerClasses.addAll(innerClasses);
|
||||
assert (nextInnerClasses.equals(innerClasses));
|
||||
Object value = null;
|
||||
|
||||
// Null
|
||||
if (jsonArray.isNull(i)) {
|
||||
value = null;
|
||||
// Lists
|
||||
} else if (innerInterfaces.contains(List.class)) {
|
||||
value = new ArrayList();
|
||||
readCollectionField(jsonArray.getJSONArray(i), (Collection) value, nextInnerClasses);
|
||||
// Sets
|
||||
} else if (innerInterfaces.contains(Set.class)) {
|
||||
value = new HashSet();
|
||||
readCollectionField(jsonArray.getJSONArray(i), (Collection) value, nextInnerClasses);
|
||||
// Maps
|
||||
} else if (innerInterfaces.contains(Map.class)) {
|
||||
value = new HashMap();
|
||||
readMapField(jsonArray.getJSONObject(i), (Map) value, nextInnerClasses);
|
||||
// Values
|
||||
} else {
|
||||
String jsonString = jsonArray.getString(i);
|
||||
value = JSONUtil.getPrimitiveValue(jsonString, innerClass);
|
||||
}
|
||||
collection.add(value);
|
||||
} // FOR
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param jsonObject
|
||||
* @param jsonKey
|
||||
* @param fieldHandle
|
||||
* @param object
|
||||
* @throws Exception
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void readFieldValue(
|
||||
final JSONObject jsonObject, final String jsonKey, Field fieldHandle, Object object)
|
||||
throws Exception {
|
||||
assert (jsonObject.has(jsonKey)) : "No entry exists for '" + jsonKey + "'";
|
||||
Class<?> fieldClass = fieldHandle.getType();
|
||||
Object fieldObject = fieldHandle.get(object);
|
||||
// String field_name = field_handle.getName();
|
||||
|
||||
// Null
|
||||
if (jsonObject.isNull(jsonKey)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Field " + jsonKey + " is null");
|
||||
}
|
||||
fieldHandle.set(object, null);
|
||||
|
||||
// Collections
|
||||
} else if (ClassUtil.getInterfaces(fieldClass).contains(Collection.class)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Field " + jsonKey + " is a collection");
|
||||
}
|
||||
assert (fieldObject != null);
|
||||
Stack<Class> innerClasses = new Stack<Class>();
|
||||
innerClasses.addAll(ClassUtil.getGenericTypes(fieldHandle));
|
||||
Collections.reverse(innerClasses);
|
||||
|
||||
JSONArray jsonInner = jsonObject.getJSONArray(jsonKey);
|
||||
if (jsonInner == null) {
|
||||
throw new JSONException("No array exists for '" + jsonKey + "'");
|
||||
}
|
||||
readCollectionField(jsonInner, (Collection) fieldObject, innerClasses);
|
||||
|
||||
// Maps
|
||||
} else if (fieldObject instanceof Map) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Field " + jsonKey + " is a map");
|
||||
}
|
||||
assert (fieldObject != null);
|
||||
Stack<Class> innerClasses = new Stack<Class>();
|
||||
innerClasses.addAll(ClassUtil.getGenericTypes(fieldHandle));
|
||||
Collections.reverse(innerClasses);
|
||||
|
||||
JSONObject jsonInner = jsonObject.getJSONObject(jsonKey);
|
||||
if (jsonInner == null) {
|
||||
throw new JSONException("No object exists for '" + jsonKey + "'");
|
||||
}
|
||||
readMapField(jsonInner, (Map) fieldObject, innerClasses);
|
||||
|
||||
// Everything else...
|
||||
} else {
|
||||
Class explicitFieldClass = JSONUtil.getClassForField(jsonObject, jsonKey);
|
||||
if (explicitFieldClass != null) {
|
||||
fieldClass = explicitFieldClass;
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug(
|
||||
"Found explict field class " + fieldClass.getSimpleName() + " for " + jsonKey);
|
||||
}
|
||||
}
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Field " + jsonKey + " is primitive type " + fieldClass.getSimpleName());
|
||||
}
|
||||
Object value = JSONUtil.getPrimitiveValue(jsonObject.getString(jsonKey), fieldClass);
|
||||
fieldHandle.set(object, value);
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Set field " + jsonKey + " to '" + value + "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given enum, load in the values from the JSON object into the current object This will
|
||||
* throw errors if a field is missing
|
||||
*
|
||||
* @param <E>
|
||||
* @param jsonObject
|
||||
* @param members
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static <E extends Enum<?>, T> void fieldsFromJSON(
|
||||
JSONObject jsonObject, T object, Class<? extends T> baseClass, E... members)
|
||||
throws JSONException {
|
||||
JSONUtil.fieldsFromJSON(jsonObject, object, baseClass, false, members);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given enum, load in the values from the JSON object into the current object If
|
||||
* ignore_missing is false, then JSONUtil will not throw an error if a field is missing
|
||||
*
|
||||
* @param <E>
|
||||
* @param <T>
|
||||
* @param jsonObject
|
||||
* @param object
|
||||
* @param baseClass
|
||||
* @param ignoreMissing
|
||||
* @param members
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static <E extends Enum<?>, T> void fieldsFromJSON(
|
||||
JSONObject jsonObject,
|
||||
T object,
|
||||
Class<? extends T> baseClass,
|
||||
boolean ignoreMissing,
|
||||
E... members)
|
||||
throws JSONException {
|
||||
try {
|
||||
fieldsFromJSON(
|
||||
jsonObject,
|
||||
object,
|
||||
baseClass,
|
||||
ignoreMissing,
|
||||
ClassUtil.getFieldsFromMembersEnum(baseClass, members));
|
||||
} catch (NoSuchFieldException ex) {
|
||||
throw new JSONException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given list of Fields, load in the values from the JSON object into the current object
|
||||
* If ignore_missing is false, then JSONUtil will not throw an error if a field is missing
|
||||
*
|
||||
* @param <E>
|
||||
* @param <T>
|
||||
* @param jsonObject
|
||||
* @param object
|
||||
* @param baseClass
|
||||
* @param ignoreMissing
|
||||
* @param fields
|
||||
* @throws JSONException
|
||||
*/
|
||||
public static <E extends Enum<?>, T> void fieldsFromJSON(
|
||||
JSONObject jsonObject,
|
||||
T object,
|
||||
Class<? extends T> baseClass,
|
||||
boolean ignoreMissing,
|
||||
Field... fields)
|
||||
throws JSONException {
|
||||
for (Field fieldHandle : fields) {
|
||||
String jsonKey = fieldHandle.getName().toUpperCase();
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Retreiving value for field '" + jsonKey + "'");
|
||||
}
|
||||
|
||||
if (!jsonObject.has(jsonKey)) {
|
||||
String msg =
|
||||
"JSONObject for "
|
||||
+ baseClass.getSimpleName()
|
||||
+ " does not have key '"
|
||||
+ jsonKey
|
||||
+ "': "
|
||||
+ CollectionUtil.list(jsonObject.keys());
|
||||
if (ignoreMissing) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.warn(msg);
|
||||
}
|
||||
continue;
|
||||
} else {
|
||||
throw new JSONException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
readFieldValue(jsonObject, jsonKey, fieldHandle, object);
|
||||
} catch (Exception ex) {
|
||||
// System.err.println(field_class + ": " + ClassUtil.getSuperClasses(field_class));
|
||||
LOG.error(
|
||||
"Unable to deserialize field '" + jsonKey + "' from " + baseClass.getSimpleName(),
|
||||
ex);
|
||||
throw new JSONException(ex);
|
||||
}
|
||||
} // FOR
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the class of a field if it was stored in the JSONObject along with the value If there is
|
||||
* no class information, then this will return null
|
||||
*
|
||||
* @param jsonObject
|
||||
* @param jsonKey
|
||||
* @return
|
||||
* @throws JSONException
|
||||
*/
|
||||
private static Class<?> getClassForField(JSONObject jsonObject, String jsonKey)
|
||||
throws JSONException {
|
||||
Class<?> fieldClass = null;
|
||||
// Check whether we also stored the class
|
||||
if (jsonObject.has(jsonKey + JSON_CLASS_SUFFIX)) {
|
||||
try {
|
||||
fieldClass = ClassUtil.getClass(jsonObject.getString(jsonKey + JSON_CLASS_SUFFIX));
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Failed to include class for field '" + jsonKey + "'", ex);
|
||||
throw new JSONException(ex);
|
||||
}
|
||||
}
|
||||
return (fieldClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the proper serialization string for the given value
|
||||
*
|
||||
* @param fieldClass
|
||||
* @param fieldValue
|
||||
* @return
|
||||
*/
|
||||
private static Object makePrimitiveValue(Class<?> fieldClass, Object fieldValue) {
|
||||
Object value = null;
|
||||
|
||||
// Class
|
||||
if (fieldClass.equals(Class.class)) {
|
||||
value = ((Class<?>) fieldValue).getName();
|
||||
// JSONSerializable
|
||||
} else if (ClassUtil.getInterfaces(fieldClass).contains(JSONSerializable.class)) {
|
||||
// Just return the value back. The JSON library will take care of it
|
||||
// System.err.println(field_class + ": " + field_value);
|
||||
value = fieldValue;
|
||||
// Everything else
|
||||
} else {
|
||||
value = fieldValue; // .toString();
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
/**
|
||||
* For the given JSON string, figure out what kind of object it is and return it
|
||||
*
|
||||
* @param jsonValue
|
||||
* @param fieldClass
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Object getPrimitiveValue(String jsonValue, Class<?> fieldClass) throws Exception {
|
||||
Object value = null;
|
||||
|
||||
// Class
|
||||
if (fieldClass.equals(Class.class)) {
|
||||
value = ClassUtil.getClass(jsonValue);
|
||||
if (value == null) {
|
||||
throw new JSONException("Failed to get class from '" + jsonValue + "'");
|
||||
}
|
||||
// Enum
|
||||
} else if (fieldClass.isEnum()) {
|
||||
for (Object o : fieldClass.getEnumConstants()) {
|
||||
Enum<?> e = (Enum<?>) o;
|
||||
if (jsonValue.equals(e.name())) {
|
||||
return (e);
|
||||
}
|
||||
} // FOR
|
||||
throw new JSONException(
|
||||
"Invalid enum value '"
|
||||
+ jsonValue
|
||||
+ "': "
|
||||
+ Arrays.toString(fieldClass.getEnumConstants()));
|
||||
// JSONSerializable
|
||||
} else if (ClassUtil.getInterfaces(fieldClass).contains(JSONSerializable.class)) {
|
||||
value = ClassUtil.newInstance(fieldClass, null, null);
|
||||
((JSONSerializable) value).fromJSON(new JSONObject(jsonValue));
|
||||
// Boolean
|
||||
} else if (fieldClass.equals(Boolean.class) || fieldClass.equals(boolean.class)) {
|
||||
// We have to use field_class.equals() because the value may be null
|
||||
value = Boolean.parseBoolean(jsonValue);
|
||||
// Short
|
||||
} else if (fieldClass.equals(Short.class) || fieldClass.equals(short.class)) {
|
||||
value = Short.parseShort(jsonValue);
|
||||
// Integer
|
||||
} else if (fieldClass.equals(Integer.class) || fieldClass.equals(int.class)) {
|
||||
value = Integer.parseInt(jsonValue);
|
||||
// Long
|
||||
} else if (fieldClass.equals(Long.class) || fieldClass.equals(long.class)) {
|
||||
value = Long.parseLong(jsonValue);
|
||||
// Float
|
||||
} else if (fieldClass.equals(Float.class) || fieldClass.equals(float.class)) {
|
||||
value = Float.parseFloat(jsonValue);
|
||||
// Double
|
||||
} else if (fieldClass.equals(Double.class) || fieldClass.equals(double.class)) {
|
||||
value = Double.parseDouble(jsonValue);
|
||||
// String
|
||||
} else if (fieldClass.equals(String.class)) {
|
||||
value = jsonValue.toString();
|
||||
}
|
||||
return (value);
|
||||
}
|
||||
|
||||
public static Class<?> getPrimitiveType(String jsonValue) {
|
||||
Object value = null;
|
||||
|
||||
// CHECKSTYLE:OFF
|
||||
// Class
|
||||
try {
|
||||
value = ClassUtil.getClass(jsonValue);
|
||||
if (value != null) return (Class.class);
|
||||
} catch (Throwable ex) {
|
||||
} // IGNORE
|
||||
|
||||
// Short
|
||||
try {
|
||||
value = Short.parseShort(jsonValue);
|
||||
return (Short.class);
|
||||
} catch (NumberFormatException ex) {
|
||||
} // IGNORE
|
||||
|
||||
// Integer
|
||||
try {
|
||||
value = Integer.parseInt(jsonValue);
|
||||
return (Integer.class);
|
||||
} catch (NumberFormatException ex) {
|
||||
} // IGNORE
|
||||
|
||||
// Long
|
||||
try {
|
||||
value = Long.parseLong(jsonValue);
|
||||
return (Long.class);
|
||||
} catch (NumberFormatException ex) {
|
||||
} // IGNORE
|
||||
|
||||
// Float
|
||||
try {
|
||||
value = Float.parseFloat(jsonValue);
|
||||
return (Float.class);
|
||||
} catch (NumberFormatException ex) {
|
||||
} // IGNORE
|
||||
|
||||
// Double
|
||||
try {
|
||||
value = Double.parseDouble(jsonValue);
|
||||
return (Double.class);
|
||||
} catch (NumberFormatException ex) {
|
||||
} // IGNORE
|
||||
// CHECKSTYLE:ON
|
||||
|
||||
// Boolean
|
||||
if (jsonValue.equalsIgnoreCase("true") || jsonValue.equalsIgnoreCase("false")) {
|
||||
return (Boolean.class);
|
||||
}
|
||||
|
||||
// Default: String
|
||||
return (String.class);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* OtterTune - ValidationUtils.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.github.fge.jackson.JsonLoader;
|
||||
import com.github.fge.jsonschema.core.exceptions.ProcessingException;
|
||||
import com.github.fge.jsonschema.core.report.ProcessingMessage;
|
||||
import com.github.fge.jsonschema.core.report.ProcessingReport;
|
||||
import com.github.fge.jsonschema.main.JsonSchema;
|
||||
import com.github.fge.jsonschema.main.JsonSchemaFactory;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
public class ValidationUtils {
|
||||
|
||||
public static final String JSON_V4_SCHEMA_IDENTIFIER = "http://json-schema.org/draft-04/schema#";
|
||||
public static final String JSON_SCHEMA_IDENTIFIER_ELEMENT = "$schema";
|
||||
|
||||
public static JsonNode getJsonNode(String jsonText) throws IOException {
|
||||
return JsonLoader.fromString(jsonText);
|
||||
} // getJsonNode(text) ends
|
||||
|
||||
public static JsonNode getJsonNode(File jsonFile) throws IOException {
|
||||
return JsonLoader.fromFile(jsonFile);
|
||||
} // getJsonNode(File) ends
|
||||
|
||||
public static JsonNode getJsonNode(URL url) throws IOException {
|
||||
return JsonLoader.fromURL(url);
|
||||
} // getJsonNode(URL) ends
|
||||
|
||||
public static JsonNode getJsonNodeFromResource(String resource) throws IOException {
|
||||
return JsonLoader.fromResource(resource);
|
||||
} // getJsonNode(Resource) ends
|
||||
|
||||
public static JsonSchema getSchemaNode(String schemaText)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonNode schemaNode = getJsonNode(schemaText);
|
||||
return getSchemaNodeHelper(schemaNode);
|
||||
} // getSchemaNode(text) ends
|
||||
|
||||
public static JsonSchema getSchemaNode(File schemaFile) throws IOException, ProcessingException {
|
||||
final JsonNode schemaNode = getJsonNode(schemaFile);
|
||||
return getSchemaNodeHelper(schemaNode);
|
||||
} // getSchemaNode(File) ends
|
||||
|
||||
public static JsonSchema getSchemaNode(URL schemaFile) throws IOException, ProcessingException {
|
||||
final JsonNode schemaNode = getJsonNode(schemaFile);
|
||||
return getSchemaNodeHelper(schemaNode);
|
||||
} // getSchemaNode(URL) ends
|
||||
|
||||
public static JsonSchema getSchemaNodeFromResource(String resource)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonNode schemaNode = getJsonNodeFromResource(resource);
|
||||
return getSchemaNodeHelper(schemaNode);
|
||||
} // getSchemaNode() ends
|
||||
|
||||
public static void validateJson(String schemaText, String jsonText)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaText);
|
||||
final JsonNode jsonNode = getJsonNode(jsonText);
|
||||
validateJson(schemaNode, jsonNode);
|
||||
} // validateJson(text) ends
|
||||
|
||||
public static void validateJson(File schemaFile, File jsonFile)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaFile);
|
||||
final JsonNode jsonNode = getJsonNode(jsonFile);
|
||||
validateJson(schemaNode, jsonNode);
|
||||
} // validateJson(File) ends
|
||||
|
||||
public static void validateJson(JsonSchema jsonSchemaNode, JsonNode jsonNode)
|
||||
throws ProcessingException {
|
||||
ProcessingReport report = jsonSchemaNode.validate(jsonNode);
|
||||
if (!report.isSuccess()) {
|
||||
for (ProcessingMessage processingMessage : report) {
|
||||
throw new ProcessingException(processingMessage);
|
||||
}
|
||||
}
|
||||
} // validateJson(Node) ends
|
||||
|
||||
public static void validateJson(URL schemaDocument, URL jsonDocument)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaDocument);
|
||||
final JsonNode jsonNode = getJsonNode(jsonDocument);
|
||||
validateJson(schemaNode, jsonNode);
|
||||
} // validateJson(URL) ends
|
||||
|
||||
public static boolean isJsonValid(JsonSchema jsonSchemaNode, JsonNode jsonNode)
|
||||
throws ProcessingException {
|
||||
ProcessingReport report = jsonSchemaNode.validate(jsonNode);
|
||||
return report.isSuccess();
|
||||
} // validateJson(Node) ends
|
||||
|
||||
public static boolean isJsonValid(String schemaText, String jsonText)
|
||||
throws ProcessingException, IOException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaText);
|
||||
final JsonNode jsonNode = getJsonNode(jsonText);
|
||||
return isJsonValid(schemaNode, jsonNode);
|
||||
} // validateJson(Node) ends
|
||||
|
||||
public static boolean isJsonValid(File schemaFile, File jsonFile)
|
||||
throws ProcessingException, IOException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaFile);
|
||||
final JsonNode jsonNode = getJsonNode(jsonFile);
|
||||
return isJsonValid(schemaNode, jsonNode);
|
||||
} // validateJson(Node) ends
|
||||
|
||||
public static boolean isJsonValid(URL schemaURL, URL jsonURL)
|
||||
throws ProcessingException, IOException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaURL);
|
||||
final JsonNode jsonNode = getJsonNode(jsonURL);
|
||||
return isJsonValid(schemaNode, jsonNode);
|
||||
} // validateJson(Node) ends
|
||||
|
||||
public static void validateJsonResource(String schemaResource, String jsonResource)
|
||||
throws IOException, ProcessingException {
|
||||
final JsonSchema schemaNode = getSchemaNode(schemaResource);
|
||||
final JsonNode jsonNode = getJsonNodeFromResource(jsonResource);
|
||||
validateJson(schemaNode, jsonNode);
|
||||
} // validateJsonResource() ends
|
||||
|
||||
private static JsonSchema getSchemaNodeHelper(JsonNode jsonNode) throws ProcessingException {
|
||||
final JsonNode schemaIdentifier = jsonNode.get(JSON_SCHEMA_IDENTIFIER_ELEMENT);
|
||||
if (null == schemaIdentifier) {
|
||||
((ObjectNode) jsonNode).put(JSON_SCHEMA_IDENTIFIER_ELEMENT, JSON_V4_SCHEMA_IDENTIFIER);
|
||||
}
|
||||
|
||||
final JsonSchemaFactory factory = JsonSchemaFactory.byDefault();
|
||||
return factory.getJsonSchema(jsonNode);
|
||||
} // getSchemaNodeHelper() ends
|
||||
}
|
||||
@@ -0,0 +1,862 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package com.controller.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A JSONArray is an ordered sequence of values. Its external text form is a string wrapped in
|
||||
* square brackets with commas separating the values. The internal form is an object having <code>
|
||||
* get</code> and <code>opt</code> methods for accessing the values by index, and <code>put</code>
|
||||
* methods for adding or replacing values. The values can be any of these types: <code>Boolean
|
||||
* </code>, <code>JSONArray</code>, <code>JSONObject</code>, <code>Number</code>, <code>String
|
||||
* </code>, or the <code>JSONObject.NULL object</code>.
|
||||
*
|
||||
* <p>The constructor can convert a JSON text into a Java object. The <code>toString</code> method
|
||||
* converts to JSON text.
|
||||
*
|
||||
* <p>A <code>get</code> method returns a value if one can be found, and throws an exception if one
|
||||
* cannot be found. An <code>opt</code> method returns a default value instead of throwing an
|
||||
* exception, and so is useful for obtaining optional values.
|
||||
*
|
||||
* <p>The generic <code>get()</code> and <code>opt()</code> methods return an object which you can
|
||||
* cast or query for type. There are also typed <code>get</code> and <code>opt</code> methods that
|
||||
* do type checking and type coersion for you.
|
||||
*
|
||||
* <p>The texts produced by the <code>toString</code> methods strictly conform to JSON syntax rules.
|
||||
* The constructors are more forgiving in the texts they will accept:
|
||||
*
|
||||
* <ul>
|
||||
* <li>An extra <code>,</code> <small>(comma)</small> may appear just before the closing
|
||||
* bracket.
|
||||
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
|
||||
* <small>(comma)</small> elision.
|
||||
* <li>Strings may be quoted with <code>'</code> <small>(single quote)</small>.
|
||||
* <li>Strings do not need to be quoted at all if they do not begin with a quote or single quote,
|
||||
* and if they do not contain leading or trailing spaces, and if they do not contain any of
|
||||
* these characters: <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers
|
||||
* and if they are not the reserved words <code>true</code>, <code>false</code>, or <code>null
|
||||
* </code>.
|
||||
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as well as by <code>,
|
||||
* </code> <small>(comma)</small>.
|
||||
* <li>Numbers may have the <code>0-</code> <small>(octal)</small> or <code>0x-</code>
|
||||
* <small>(hex)</small> prefix.
|
||||
* <li>Comments written in the slashshlash, slashstar, and hash conventions will be ignored.
|
||||
* </ul>
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONArray {
|
||||
|
||||
/** The arrayList where the JSONArray's properties are kept. */
|
||||
private ArrayList<Object> myArrayList;
|
||||
|
||||
/** Construct an empty JSONArray. */
|
||||
public JSONArray() {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a JSONTokener.
|
||||
*
|
||||
* @param x A JSONTokener
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(JSONTokener x) throws JSONException {
|
||||
this();
|
||||
char c = x.nextClean();
|
||||
char q;
|
||||
if (c == '[') {
|
||||
q = ']';
|
||||
} else if (c == '(') {
|
||||
q = ')';
|
||||
} else {
|
||||
throw x.syntaxError("A JSONArray text must start with '['");
|
||||
}
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
for (; ; ) {
|
||||
if (x.nextClean() == ',') {
|
||||
x.back();
|
||||
this.myArrayList.add(null);
|
||||
} else {
|
||||
x.back();
|
||||
this.myArrayList.add(x.nextValue());
|
||||
}
|
||||
c = x.nextClean();
|
||||
switch (c) {
|
||||
case ';':
|
||||
case ',':
|
||||
if (x.nextClean() == ']') {
|
||||
return;
|
||||
}
|
||||
x.back();
|
||||
break;
|
||||
case ']':
|
||||
case ')':
|
||||
if (q != c) {
|
||||
throw x.syntaxError("Expected a '" + new Character(q) + "'");
|
||||
}
|
||||
return;
|
||||
default:
|
||||
throw x.syntaxError("Expected a ',' or ']'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a source JSON text.
|
||||
*
|
||||
* @param source A string that begins with <code>[</code> <small>(left bracket)</small> and
|
||||
* ends with <code>]</code> <small>(right bracket)</small>.
|
||||
* @throws JSONException If there is a syntax error.
|
||||
*/
|
||||
public JSONArray(String source) throws JSONException {
|
||||
this(new JSONTokener(source));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from a Collection.
|
||||
*
|
||||
* @param collection A Collection.
|
||||
*/
|
||||
public JSONArray(Collection<?> collection) {
|
||||
this.myArrayList =
|
||||
(collection == null) ? new ArrayList<Object>() : new ArrayList<Object>(collection);
|
||||
}
|
||||
|
||||
/** Construct a JSONArray from a collection of beans. The collection should have Java Beans. */
|
||||
public JSONArray(Collection<?> collection, boolean includeSuperClass) {
|
||||
this.myArrayList = new ArrayList<Object>();
|
||||
if (collection != null) {
|
||||
for (Iterator<?> iter = collection.iterator(); iter.hasNext(); ) {
|
||||
this.myArrayList.add(new JSONObject(iter.next(), includeSuperClass));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array
|
||||
*
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array) throws JSONException {
|
||||
this();
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(Array.get(array, i));
|
||||
}
|
||||
} else {
|
||||
throw new JSONException("JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a JSONArray from an array with a bean. The array should have Java Beans.
|
||||
*
|
||||
* @throws JSONException If not an array.
|
||||
*/
|
||||
public JSONArray(Object array, boolean includeSuperClass) throws JSONException {
|
||||
this();
|
||||
if (array.getClass().isArray()) {
|
||||
int length = Array.getLength(array);
|
||||
for (int i = 0; i < length; i += 1) {
|
||||
this.put(new JSONObject(Array.get(array, i), includeSuperClass));
|
||||
}
|
||||
} else {
|
||||
throw new JSONException("JSONArray initial value should be a string or collection or array.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public Object get(int index) throws JSONException {
|
||||
Object o = opt(index);
|
||||
if (o == null) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the boolean value associated with an index. The string values "true" and "false" are
|
||||
* converted to boolean.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
* @throws JSONException If there is no value for the index or if the value is not convertable to
|
||||
* boolean.
|
||||
*/
|
||||
public boolean getBoolean(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
if (o.equals(Boolean.FALSE)
|
||||
|| (o instanceof String && ((String) o).equalsIgnoreCase("false"))) {
|
||||
return false;
|
||||
} else if (o.equals(Boolean.TRUE)
|
||||
|| (o instanceof String && ((String) o).equalsIgnoreCase("true"))) {
|
||||
return true;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a Boolean.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the double value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
|
||||
*/
|
||||
public double getDouble(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
try {
|
||||
return o instanceof Number
|
||||
? ((Number) o).doubleValue()
|
||||
: Double.valueOf((String) o).doubleValue();
|
||||
} catch (Exception e) {
|
||||
throw new JSONException("JSONArray[" + index + "] is not a number.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the int value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
|
||||
* if the value cannot be converted to a number.
|
||||
*/
|
||||
public int getInt(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
return o instanceof Number ? ((Number) o).intValue() : (int) getDouble(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONArray associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONArray value.
|
||||
* @throws JSONException If there is no value for the index. or if the value is not a JSONArray
|
||||
*/
|
||||
public JSONArray getJSONArray(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
if (o instanceof JSONArray) {
|
||||
return (JSONArray) o;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSONObject associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONObject value.
|
||||
* @throws JSONException If there is no value for the index or if the value is not a JSONObject
|
||||
*/
|
||||
public JSONObject getJSONObject(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
if (o instanceof JSONObject) {
|
||||
return (JSONObject) o;
|
||||
}
|
||||
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the long value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
* @throws JSONException If the key is not found or if the value cannot be converted to a number.
|
||||
*/
|
||||
public long getLong(int index) throws JSONException {
|
||||
Object o = get(index);
|
||||
return o instanceof Number ? ((Number) o).longValue() : (long) getDouble(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the string associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A string value.
|
||||
* @throws JSONException If there is no value for the index.
|
||||
*/
|
||||
public String getString(int index) throws JSONException {
|
||||
return get(index).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the value is null.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return true if the value at the index is null, or if there is no value.
|
||||
*/
|
||||
public boolean isNull(int index) {
|
||||
return JSONObject.NULL.equals(opt(index));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a string from the contents of this JSONArray. The <code>separator</code> string is
|
||||
* inserted between each element. Warning: This method assumes that the data structure is
|
||||
* acyclical.
|
||||
*
|
||||
* @param separator A string that will be inserted between the elements.
|
||||
* @return a string.
|
||||
* @throws JSONException If the array contains an invalid number.
|
||||
*/
|
||||
public String join(String separator) throws JSONException {
|
||||
int len = length();
|
||||
StringBuffer sb = new StringBuffer();
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (i > 0) {
|
||||
sb.append(separator);
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of elements in the JSONArray, included nulls.
|
||||
*
|
||||
* @return The length (or size).
|
||||
*/
|
||||
public int length() {
|
||||
return this.myArrayList.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional object value associated with an index.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return An object value, or null if there is no object at that index.
|
||||
*/
|
||||
public Object opt(int index) {
|
||||
return (index < 0 || index >= length()) ? null : this.myArrayList.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns false if there is no value
|
||||
* at that index, or if the value is not Boolean.TRUE or the String "true".
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index) {
|
||||
return optBoolean(index, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional boolean value associated with an index. It returns the defaultValue if there
|
||||
* is no value at that index or if it is not a Boolean or the String "true" or "false" (case
|
||||
* insensitive).
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue A boolean default.
|
||||
* @return The truth.
|
||||
*/
|
||||
public boolean optBoolean(int index, boolean defaultValue) {
|
||||
try {
|
||||
return getBoolean(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. NaN is returned if there is no value
|
||||
* for the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index) {
|
||||
return optDouble(index, Double.NaN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional double value associated with an index. The defaultValue is returned if there
|
||||
* is no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index subscript
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public double optDouble(int index, double defaultValue) {
|
||||
try {
|
||||
return getDouble(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. Zero is returned if there is no value for
|
||||
* the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index) {
|
||||
return optInt(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional int value associated with an index. The defaultValue is returned if there is
|
||||
* no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public int optInt(int index, int defaultValue) {
|
||||
try {
|
||||
return getInt(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONArray associated with an index.
|
||||
*
|
||||
* @param index subscript
|
||||
* @return A JSONArray value, or null if the index has no value, or if the value is not a
|
||||
* JSONArray.
|
||||
*/
|
||||
public JSONArray optJSONArray(int index) {
|
||||
Object o = opt(index);
|
||||
return o instanceof JSONArray ? (JSONArray) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional JSONObject associated with an index. Null is returned if the key is not found,
|
||||
* or null if the index has no value, or if the value is not a JSONObject.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A JSONObject value.
|
||||
*/
|
||||
public JSONObject optJSONObject(int index) {
|
||||
Object o = opt(index);
|
||||
return o instanceof JSONObject ? (JSONObject) o : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. Zero is returned if there is no value for
|
||||
* the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index) {
|
||||
return optLong(index, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional long value associated with an index. The defaultValue is returned if there is
|
||||
* no value for the index, or if the value is not a number and cannot be converted to a number.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return The value.
|
||||
*/
|
||||
public long optLong(int index, long defaultValue) {
|
||||
try {
|
||||
return getLong(index);
|
||||
} catch (Exception e) {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string value associated with an index. It returns an empty string if there is
|
||||
* no value at that index. If the value is not a string and is not null, then it is coverted to a
|
||||
* string.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index) {
|
||||
return optString(index, "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the optional string associated with an index. The defaultValue is returned if the key is
|
||||
* not found.
|
||||
*
|
||||
* @param index The index must be between 0 and length() - 1.
|
||||
* @param defaultValue The default value.
|
||||
* @return A String value.
|
||||
*/
|
||||
public String optString(int index, String defaultValue) {
|
||||
Object o = opt(index);
|
||||
return o != null ? o.toString() : defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a boolean value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(boolean value) {
|
||||
put(value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
|
||||
* Collection.
|
||||
*
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Collection<?> value) {
|
||||
put(new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a double value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A double value.
|
||||
* @throws JSONException if the value is not finite.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(double value) throws JSONException {
|
||||
Double d = new Double(value);
|
||||
JSONObject.testValidity(d);
|
||||
put(d);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an int value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(int value) {
|
||||
put(new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an long value. This increases the array's length by one.
|
||||
*
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(long value) {
|
||||
put(new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject which is produced from a
|
||||
* Map.
|
||||
*
|
||||
* @param value A Map value.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Map<?, ?> value) {
|
||||
put(new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append an object value. This increases the array's length by one.
|
||||
*
|
||||
* @param value An object value. The value should be a Boolean, Double, Integer, JSONArray,
|
||||
* JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
* @return this.
|
||||
*/
|
||||
public JSONArray put(Object value) {
|
||||
this.myArrayList.add(value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a boolean value in the JSONArray. If the index is greater than the length of the
|
||||
* JSONArray, then null elements will be added as necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A boolean value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, boolean value) throws JSONException {
|
||||
put(index, value ? Boolean.TRUE : Boolean.FALSE);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONArray which is produced from a
|
||||
* Collection.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A Collection value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not finite.
|
||||
*/
|
||||
public JSONArray put(int index, Collection<?> value) throws JSONException {
|
||||
put(index, new JSONArray(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a double value. If the index is greater than the length of the JSONArray, then
|
||||
* null elements will be added as necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A double value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the value is not finite.
|
||||
*/
|
||||
public JSONArray put(int index, double value) throws JSONException {
|
||||
put(index, new Double(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an int value. If the index is greater than the length of the JSONArray, then
|
||||
* null elements will be added as necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value An int value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, int value) throws JSONException {
|
||||
put(index, new Integer(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace a long value. If the index is greater than the length of the JSONArray, then
|
||||
* null elements will be added as necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value A long value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative.
|
||||
*/
|
||||
public JSONArray put(int index, long value) throws JSONException {
|
||||
put(index, new Long(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a value in the JSONArray, where the value will be a JSONObject which is produced from a
|
||||
* Map.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The Map value.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Map<?, ?> value) throws JSONException {
|
||||
put(index, new JSONObject(value));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Put or replace an object value in the JSONArray. If the index is greater than the length of the
|
||||
* JSONArray, then null elements will be added as necessary to pad it out.
|
||||
*
|
||||
* @param index The subscript.
|
||||
* @param value The value to put into the array. The value should be a Boolean, Double, Integer,
|
||||
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
|
||||
* @return this.
|
||||
* @throws JSONException If the index is negative or if the the value is an invalid number.
|
||||
*/
|
||||
public JSONArray put(int index, Object value) throws JSONException {
|
||||
JSONObject.testValidity(value);
|
||||
if (index < 0) {
|
||||
throw new JSONException("JSONArray[" + index + "] not found.");
|
||||
}
|
||||
if (index < length()) {
|
||||
this.myArrayList.set(index, value);
|
||||
} else {
|
||||
while (index != length()) {
|
||||
put(JSONObject.NULL);
|
||||
}
|
||||
put(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a JSONObject by combining a JSONArray of names with the values of this JSONArray.
|
||||
*
|
||||
* @param names A JSONArray containing a list of key strings. These will be paired with the
|
||||
* values.
|
||||
* @return A JSONObject, or null if there are no names or if this JSONArray has no values.
|
||||
* @throws JSONException If any of the names are null.
|
||||
*/
|
||||
public JSONObject toJSONObject(JSONArray names) throws JSONException {
|
||||
if (names == null || names.length() == 0 || length() == 0) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jo = new JSONObject();
|
||||
for (int i = 0; i < names.length(); i += 1) {
|
||||
jo.put(names.getString(i), this.opt(i));
|
||||
}
|
||||
return jo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a JSON text of this JSONArray. For compactness, no unnecessary whitespace is added. If it
|
||||
* is not possible to produce a syntactically correct JSON text then null will be returned
|
||||
* instead. This could occur if the array contains an invalid number.
|
||||
*
|
||||
* <p>Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return a printable, displayable, transmittable representation of the array.
|
||||
*/
|
||||
public String toString() {
|
||||
try {
|
||||
return '[' + join(",") + ']';
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
|
||||
* structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of indentation.
|
||||
* @return a printable, displayable, transmittable representation of the object, beginning with
|
||||
* <code>[</code> <small>(left bracket)</small> and ending with <code>]</code>
|
||||
* <small>(right bracket)</small>.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public String toString(int indentFactor) throws JSONException {
|
||||
return toString(indentFactor, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a prettyprinted JSON text of this JSONArray. Warning: This method assumes that the data
|
||||
* structure is acyclical.
|
||||
*
|
||||
* @param indentFactor The number of spaces to add to each level of indentation.
|
||||
* @param indent The indention of the top level.
|
||||
* @return a printable, displayable, transmittable representation of the array.
|
||||
* @throws JSONException
|
||||
*/
|
||||
String toString(int indentFactor, int indent) throws JSONException {
|
||||
int len = length();
|
||||
if (len == 0) {
|
||||
return "[]";
|
||||
}
|
||||
int i;
|
||||
StringBuffer sb = new StringBuffer("[");
|
||||
if (len == 1) {
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(0), indentFactor, indent));
|
||||
} else {
|
||||
int newindent = indent + indentFactor;
|
||||
// sb.append('\n');
|
||||
|
||||
boolean intType = false;
|
||||
for (i = 0; i < len; i += 1) {
|
||||
if (this.myArrayList.get(i).getClass() != Integer.class) {
|
||||
if (i == 0) {
|
||||
sb.append('\n');
|
||||
}
|
||||
if (i > 0) {
|
||||
sb.append(",\n");
|
||||
}
|
||||
for (int j = 0; j < newindent; j += 1) {
|
||||
sb.append(' ');
|
||||
}
|
||||
} else {
|
||||
intType = true;
|
||||
if (i > 0) sb.append(", ");
|
||||
}
|
||||
sb.append(JSONObject.valueToString(this.myArrayList.get(i), indentFactor, newindent));
|
||||
}
|
||||
if (intType == false) {
|
||||
sb.append('\n');
|
||||
for (i = 0; i < indent; i += 1) {
|
||||
sb.append(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
sb.append(']');
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the contents of the JSONArray as JSON text to a writer. For compactness, no whitespace is
|
||||
* added.
|
||||
*
|
||||
* <p>Warning: This method assumes that the data structure is acyclical.
|
||||
*
|
||||
* @return The writer.
|
||||
* @throws JSONException
|
||||
*/
|
||||
public Writer write(Writer writer) throws JSONException {
|
||||
try {
|
||||
boolean b = false;
|
||||
int len = length();
|
||||
|
||||
writer.write('[');
|
||||
|
||||
for (int i = 0; i < len; i += 1) {
|
||||
if (b) {
|
||||
writer.write(',');
|
||||
}
|
||||
Object v = this.myArrayList.get(i);
|
||||
if (v instanceof JSONObject) {
|
||||
((JSONObject) v).write(writer);
|
||||
} else if (v instanceof JSONArray) {
|
||||
((JSONArray) v).write(writer);
|
||||
} else {
|
||||
writer.write(JSONObject.valueToString(v));
|
||||
}
|
||||
b = true;
|
||||
}
|
||||
writer.write(']');
|
||||
return writer;
|
||||
} catch (IOException e) {
|
||||
throw new JSONException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.controller.util.json;
|
||||
|
||||
/**
|
||||
* The JSONException is thrown by the JSON.org classes then things are amiss.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private Throwable cause;
|
||||
|
||||
/**
|
||||
* Constructs a JSONException with an explanatory message.
|
||||
*
|
||||
* @param message Detail about the reason for the exception.
|
||||
*/
|
||||
public JSONException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JSONException(Throwable t) {
|
||||
super(t.getMessage());
|
||||
this.cause = t;
|
||||
}
|
||||
|
||||
public Throwable getCause() {
|
||||
return this.cause;
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,16 @@
|
||||
package com.controller.util.json;
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package com.controller.util.json;
|
||||
|
||||
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 2008-09-18
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public String toString() {
|
||||
return this.mode == 'd' ? this.writer.toString() : null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package com.controller.util.json;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
|
||||
/**
|
||||
* 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 2008-09-18
|
||||
*/
|
||||
public class JSONTokener {
|
||||
|
||||
private int index;
|
||||
private Reader reader;
|
||||
private char lastChar;
|
||||
private boolean useLastChar;
|
||||
|
||||
/**
|
||||
* Construct a JSONTokener from a string.
|
||||
*
|
||||
* @param reader A reader.
|
||||
*/
|
||||
public JSONTokener(Reader reader) {
|
||||
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
|
||||
this.useLastChar = false;
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public void back() throws JSONException {
|
||||
if (useLastChar || index <= 0) {
|
||||
throw new JSONException("Stepping back two steps is not supported");
|
||||
}
|
||||
index -= 1;
|
||||
useLastChar = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the source string still contains characters that next() can consume.
|
||||
*
|
||||
* @return true if not yet at the end of the source.
|
||||
*/
|
||||
public boolean more() throws JSONException {
|
||||
char nextChar = next();
|
||||
if (nextChar == 0) {
|
||||
return false;
|
||||
}
|
||||
back();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next character in the source string.
|
||||
*
|
||||
* @return The next character, or 0 if past the end of the source string.
|
||||
*/
|
||||
public char next() throws JSONException {
|
||||
if (this.useLastChar) {
|
||||
this.useLastChar = false;
|
||||
if (this.lastChar != 0) {
|
||||
this.index += 1;
|
||||
}
|
||||
return this.lastChar;
|
||||
}
|
||||
int c;
|
||||
try {
|
||||
c = this.reader.read();
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
if (c <= 0) { // End of stream
|
||||
this.lastChar = 0;
|
||||
return 0;
|
||||
}
|
||||
this.index += 1;
|
||||
this.lastChar = (char) c;
|
||||
return this.lastChar;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = next();
|
||||
if (n != c) {
|
||||
throw syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
|
||||
}
|
||||
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[] buffer = new char[n];
|
||||
int pos = 0;
|
||||
|
||||
if (this.useLastChar) {
|
||||
this.useLastChar = false;
|
||||
buffer[0] = this.lastChar;
|
||||
pos = 1;
|
||||
}
|
||||
|
||||
try {
|
||||
int len;
|
||||
while ((pos < n) && ((len = reader.read(buffer, pos, n - pos)) != -1)) {
|
||||
pos += len;
|
||||
}
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
this.index += pos;
|
||||
|
||||
if (pos < n) {
|
||||
throw syntaxError("Substring bounds error");
|
||||
}
|
||||
|
||||
this.lastChar = buffer[n - 1];
|
||||
return new String(buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next char in the string, skipping whitespace.
|
||||
*
|
||||
* @throws JSONException
|
||||
* @return A character, or 0 if there are no more characters.
|
||||
*/
|
||||
public char nextClean() throws JSONException {
|
||||
for (; ; ) {
|
||||
char c = 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;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
switch (c) {
|
||||
case 0:
|
||||
case '\n':
|
||||
case '\r':
|
||||
throw syntaxError("Unterminated string");
|
||||
case '\\':
|
||||
c = 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':
|
||||
sb.append((char) Integer.parseInt(next(4), 16));
|
||||
break;
|
||||
case 'x':
|
||||
sb.append((char) Integer.parseInt(next(2), 16));
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
}
|
||||
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 d A delimiter character.
|
||||
* @return A string.
|
||||
*/
|
||||
public String nextTo(char d) throws JSONException {
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
char c = next();
|
||||
if (c == d || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
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.
|
||||
*/
|
||||
public String nextTo(String delimiters) throws JSONException {
|
||||
char c;
|
||||
StringBuffer sb = new StringBuffer();
|
||||
for (; ; ) {
|
||||
c = next();
|
||||
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
|
||||
if (c != 0) {
|
||||
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 = nextClean();
|
||||
String s;
|
||||
|
||||
switch (c) {
|
||||
case '"':
|
||||
case '\'':
|
||||
return nextString(c);
|
||||
case '{':
|
||||
back();
|
||||
return new JSONObject(this);
|
||||
case '[':
|
||||
case '(':
|
||||
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.
|
||||
*/
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
|
||||
sb.append(c);
|
||||
c = next();
|
||||
}
|
||||
back();
|
||||
|
||||
s = sb.toString().trim();
|
||||
if (s.equals("")) {
|
||||
throw syntaxError("Missing value");
|
||||
}
|
||||
return JSONObject.stringToValue(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public char skipTo(char to) throws JSONException {
|
||||
char c;
|
||||
try {
|
||||
int startIndex = this.index;
|
||||
reader.mark(Integer.MAX_VALUE);
|
||||
do {
|
||||
c = next();
|
||||
if (c == 0) {
|
||||
reader.reset();
|
||||
this.index = startIndex;
|
||||
return c;
|
||||
}
|
||||
} while (c != to);
|
||||
} catch (IOException exc) {
|
||||
throw new JSONException(exc);
|
||||
}
|
||||
|
||||
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 + toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a printable string of this JSONTokener.
|
||||
*
|
||||
* @return " at character [this.index]"
|
||||
*/
|
||||
public String toString() {
|
||||
return " at character " + index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
/*
|
||||
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.
|
||||
*/
|
||||
|
||||
package com.controller.util.json;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
||||
/**
|
||||
* 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 20 levels deep.
|
||||
*
|
||||
* <p>This can sometimes be easier than using a JSONObject to build a string.
|
||||
*
|
||||
* @author JSON.org
|
||||
* @version 2008-09-18
|
||||
*/
|
||||
public class JSONWriter {
|
||||
private static final int maxdepth = 20;
|
||||
|
||||
/** 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 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 Writer writer;
|
||||
|
||||
/** Make a fresh JSONWriter. It can be used to build one JSON text. */
|
||||
public JSONWriter(Writer w) {
|
||||
this.comma = false;
|
||||
this.mode = 'i';
|
||||
this.stack = new JSONObject[maxdepth];
|
||||
this.top = 0;
|
||||
this.writer = w;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a value.
|
||||
*
|
||||
* @param s A string value.
|
||||
* @return this
|
||||
* @throws JSONException If the value is out of sequence.
|
||||
*/
|
||||
private JSONWriter append(String s) throws JSONException {
|
||||
if (s == null) {
|
||||
throw new JSONException("Null pointer");
|
||||
}
|
||||
if (this.mode == 'o' || this.mode == 'a') {
|
||||
try {
|
||||
if (this.comma && this.mode == 'a') {
|
||||
this.writer.write(',');
|
||||
}
|
||||
this.writer.write(s);
|
||||
} catch (IOException e) {
|
||||
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 == 'o' ? "Misplaced endObject." : "Misplaced endArray.");
|
||||
}
|
||||
this.pop(m);
|
||||
try {
|
||||
this.writer.write(c);
|
||||
} catch (IOException e) {
|
||||
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 s 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 s) throws JSONException {
|
||||
if (s == null) {
|
||||
throw new JSONException("Null key.");
|
||||
}
|
||||
if (this.mode == 'k') {
|
||||
try {
|
||||
if (this.comma) {
|
||||
this.writer.write(',');
|
||||
}
|
||||
stack[top - 1].putOnce(s, Boolean.TRUE);
|
||||
this.writer.write(JSONObject.quote(s));
|
||||
this.writer.write(':');
|
||||
this.comma = false;
|
||||
this.mode = 'o';
|
||||
return this;
|
||||
} catch (IOException e) {
|
||||
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 c The scope to open.
|
||||
* @throws JSONException If nesting is too deep.
|
||||
*/
|
||||
private void push(JSONObject jo) throws JSONException {
|
||||
if (this.top >= maxdepth) {
|
||||
throw new JSONException("Nesting too deep.");
|
||||
}
|
||||
this.stack[this.top] = jo;
|
||||
this.mode = jo == null ? 'a' : 'k';
|
||||
this.top += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Append either the value <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(new Double(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 o The object to append. It can be null, or a Boolean, Number, String, JSONObject, or
|
||||
* JSONArray, or an object with a toJSONString() method.
|
||||
* @return this
|
||||
* @throws JSONException If the value is out of sequence.
|
||||
*/
|
||||
public JSONWriter value(Object o) throws JSONException {
|
||||
return this.append(JSONObject.valueToString(o));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,440 @@
|
||||
package com.controller.util.json;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Test class. This file is not formally a member of the com.controller.util.json library. It is
|
||||
* just a casual test tool.
|
||||
*/
|
||||
public class Test {
|
||||
|
||||
/**
|
||||
* Entry point.
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
Iterator<?> it;
|
||||
JSONArray a;
|
||||
JSONObject j;
|
||||
JSONStringer jj;
|
||||
String s;
|
||||
|
||||
/**
|
||||
* Obj is a typical class that implements JSONString. It also provides some beanie methods that
|
||||
* can be used to construct a JSONObject. It also demonstrates constructing a JSONObject with an
|
||||
* array of names.
|
||||
*/
|
||||
class Obj implements JSONString {
|
||||
public String aString;
|
||||
public double aNumber;
|
||||
public boolean aBoolean;
|
||||
|
||||
public Obj(String string, double n, boolean b) {
|
||||
this.aString = string;
|
||||
this.aNumber = n;
|
||||
this.aBoolean = b;
|
||||
}
|
||||
|
||||
public double getNumber() {
|
||||
return this.aNumber;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return this.aString;
|
||||
}
|
||||
|
||||
public boolean isBoolean() {
|
||||
return this.aBoolean;
|
||||
}
|
||||
|
||||
public String getBENT() {
|
||||
return "All uppercase key";
|
||||
}
|
||||
|
||||
public String getX() {
|
||||
return "x";
|
||||
}
|
||||
|
||||
public String toJSONString() {
|
||||
return "{"
|
||||
+ JSONObject.quote(this.aString)
|
||||
+ ":"
|
||||
+ JSONObject.doubleToString(this.aNumber)
|
||||
+ "}";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.getString()
|
||||
+ " "
|
||||
+ this.getNumber()
|
||||
+ " "
|
||||
+ this.isBoolean()
|
||||
+ "."
|
||||
+ this.getBENT()
|
||||
+ " "
|
||||
+ this.getX();
|
||||
}
|
||||
}
|
||||
|
||||
Obj obj = new Obj("A beany object", 42, true);
|
||||
|
||||
try {
|
||||
|
||||
s =
|
||||
"{ \"entity\": { \"imageURL\": \"\", \"name\": \"IXXXXXXXXXXXXX\", \"id\": 12336, \"ratingCount\": null, \"averageRating\": null } }";
|
||||
j = new JSONObject(s);
|
||||
System.out.println(j.toString(2));
|
||||
|
||||
jj = new JSONStringer();
|
||||
s =
|
||||
jj.object()
|
||||
.key("single")
|
||||
.value("MARIE HAA'S")
|
||||
.key("Johnny")
|
||||
.value("MARIE HAA\\'S")
|
||||
.key("foo")
|
||||
.value("bar")
|
||||
.key("baz")
|
||||
.array()
|
||||
.object()
|
||||
.key("quux")
|
||||
.value("Thanks, Josh!")
|
||||
.endObject()
|
||||
.endArray()
|
||||
.key("obj keys")
|
||||
.value(JSONObject.getNames(obj))
|
||||
.endObject()
|
||||
.toString();
|
||||
System.out.println(s);
|
||||
|
||||
System.out.println(
|
||||
new JSONStringer()
|
||||
.object()
|
||||
.key("a")
|
||||
.array()
|
||||
.array()
|
||||
.array()
|
||||
.value("b")
|
||||
.endArray()
|
||||
.endArray()
|
||||
.endArray()
|
||||
.endObject()
|
||||
.toString());
|
||||
|
||||
jj = new JSONStringer();
|
||||
jj.array();
|
||||
jj.value(1);
|
||||
jj.array();
|
||||
jj.value(null);
|
||||
jj.array();
|
||||
jj.object();
|
||||
jj.key("empty-array").array().endArray();
|
||||
jj.key("answer").value(42);
|
||||
jj.key("null").value(null);
|
||||
jj.key("false").value(false);
|
||||
jj.key("true").value(true);
|
||||
jj.key("big").value(123456789e+88);
|
||||
jj.key("small").value(123456789e-88);
|
||||
jj.key("empty-object").object().endObject();
|
||||
jj.key("long");
|
||||
jj.value(9223372036854775807L);
|
||||
jj.endObject();
|
||||
jj.value("two");
|
||||
jj.endArray();
|
||||
jj.value(true);
|
||||
jj.endArray();
|
||||
jj.value(98.6);
|
||||
jj.value(-100.0);
|
||||
jj.object();
|
||||
jj.endObject();
|
||||
jj.object();
|
||||
jj.key("one");
|
||||
jj.value(1.00);
|
||||
jj.endObject();
|
||||
jj.value(obj);
|
||||
jj.endArray();
|
||||
System.out.println(jj.toString());
|
||||
|
||||
System.out.println(new JSONArray(jj.toString()).toString(4));
|
||||
|
||||
int ar[] = {1, 2, 3};
|
||||
JSONArray ja = new JSONArray(ar);
|
||||
System.out.println(ja.toString());
|
||||
|
||||
String sa[] = {"aString", "aNumber", "aBoolean"};
|
||||
j = new JSONObject(obj, sa);
|
||||
j.put("Testing JSONString interface", obj);
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{slashes: '///', closetag: '</script>', backslash:'\\\\', ei: {quotes: '\"\\''},eo: {a: '\"quoted\"', b:\"don't\"}, quotes: [\"'\", '\"']}");
|
||||
System.out.println(j.toString(2));
|
||||
System.out.println("");
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{foo: [true, false,9876543210, 0.0, 1.00000001, 1.000000000001, 1.00000000000000001,"
|
||||
+ " .00000000000000001, 2.00, 0.1, 2e100, -32,[],{}, \"string\"], "
|
||||
+ " to : null, op : 'Good',"
|
||||
+ "ten:10} postfix comment");
|
||||
j.put("String", "98.6");
|
||||
j.put("JSONObject", new JSONObject());
|
||||
j.put("JSONArray", new JSONArray());
|
||||
j.put("int", 57);
|
||||
j.put("double", 123456789012345678901234567890.);
|
||||
j.put("true", true);
|
||||
j.put("false", false);
|
||||
j.put("null", JSONObject.NULL);
|
||||
j.put("bool", "true");
|
||||
j.put("zero", -0.0);
|
||||
j.put("\\u2028", "\u2028");
|
||||
j.put("\\u2029", "\u2029");
|
||||
a = j.getJSONArray("foo");
|
||||
a.put(666);
|
||||
a.put(2001.99);
|
||||
a.put("so \"fine\".");
|
||||
a.put("so <fine>.");
|
||||
a.put(true);
|
||||
a.put(false);
|
||||
a.put(new JSONArray());
|
||||
a.put(new JSONObject());
|
||||
j.put("keys", JSONObject.getNames(j));
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
System.out.println("String: " + j.getDouble("String"));
|
||||
System.out.println(" bool: " + j.getBoolean("bool"));
|
||||
System.out.println(" to: " + j.getString("to"));
|
||||
System.out.println(" true: " + j.getString("true"));
|
||||
System.out.println(" foo: " + j.getJSONArray("foo"));
|
||||
System.out.println(" op: " + j.getString("op"));
|
||||
System.out.println(" ten: " + j.getInt("ten"));
|
||||
System.out.println(" oops: " + j.optBoolean("oops"));
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{nix: null, nux: false, null: 'null', 'Request-URI': '/', Method: 'GET', 'HTTP-Version': 'HTTP/1.0'}");
|
||||
System.out.println(j.toString(2));
|
||||
System.out.println("isNull: " + j.isNull("nix"));
|
||||
System.out.println(" has: " + j.has("nix"));
|
||||
System.out.println("");
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{Envelope: {Body: {\"ns1:doGoogleSearch\": {oe: \"latin1\", filter: true, q: \"'+search+'\", key: \"GOOGLEKEY\", maxResults: 10, \"SOAP-ENV:encodingStyle\": \"http://schemas.xmlsoap.org/soap/encoding/\", start: 0, ie: \"latin1\", safeSearch:false, \"xmlns:ns1\": \"urn:GoogleSearch\"}}}}");
|
||||
System.out.println(j.toString(2));
|
||||
System.out.println("");
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{script: 'It is not allowed in HTML to send a close script tag in a string<script>because it confuses browsers</script>so we insert a backslash before the /'}");
|
||||
System.out.println(j.toString());
|
||||
System.out.println("");
|
||||
|
||||
JSONTokener jt =
|
||||
new JSONTokener("{op:'test', to:'session', pre:1}{op:'test', to:'session', pre:2}");
|
||||
j = new JSONObject(jt);
|
||||
System.out.println(j.toString());
|
||||
System.out.println("pre: " + j.optInt("pre"));
|
||||
int i = jt.skipTo('{');
|
||||
System.out.println(i);
|
||||
j = new JSONObject(jt);
|
||||
System.out.println(j.toString());
|
||||
System.out.println("");
|
||||
|
||||
a = new JSONArray(" [\"<escape>\", next is an implied null , , ok,] ");
|
||||
System.out.println(a.toString());
|
||||
System.out.println("");
|
||||
System.out.println("");
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{ fun => with non-standard forms ; forgiving => This package can be used to parse formats that are similar to but not stricting conforming to JSON; why=To make it easier to migrate existing data to JSON,one = [[1.00]]; uno=[[{1=>1}]];'+':+6e66 ;pluses=+++;empty = '' , 'double':0.666,true: TRUE, false: FALSE, null=NULL;[true] = [[!,@;*]]; string=> o. k. ; \r oct=0666; hex=0x666; dec=666; o=0999; noh=0x0x}");
|
||||
System.out.println(j.toString(4));
|
||||
System.out.println("");
|
||||
if (j.getBoolean("true") && !j.getBoolean("false")) {
|
||||
System.out.println("It's all good");
|
||||
}
|
||||
|
||||
System.out.println("");
|
||||
j = new JSONObject(j, new String[] {"dec", "oct", "hex", "missing"});
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
System.out.println("");
|
||||
System.out.println(new JSONStringer().array().value(a).value(j).endArray());
|
||||
|
||||
j =
|
||||
new JSONObject(
|
||||
"{string: \"98.6\", long: 2147483648, int: 2147483647, longer: 9223372036854775807, double: 9223372036854775808}");
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
System.out.println("\ngetInt");
|
||||
System.out.println("int " + j.getInt("int"));
|
||||
System.out.println("long " + j.getInt("long"));
|
||||
System.out.println("longer " + j.getInt("longer"));
|
||||
System.out.println("double " + j.getInt("double"));
|
||||
System.out.println("string " + j.getInt("string"));
|
||||
|
||||
System.out.println("\ngetLong");
|
||||
System.out.println("int " + j.getLong("int"));
|
||||
System.out.println("long " + j.getLong("long"));
|
||||
System.out.println("longer " + j.getLong("longer"));
|
||||
System.out.println("double " + j.getLong("double"));
|
||||
System.out.println("string " + j.getLong("string"));
|
||||
|
||||
System.out.println("\ngetDouble");
|
||||
System.out.println("int " + j.getDouble("int"));
|
||||
System.out.println("long " + j.getDouble("long"));
|
||||
System.out.println("longer " + j.getDouble("longer"));
|
||||
System.out.println("double " + j.getDouble("double"));
|
||||
System.out.println("string " + j.getDouble("string"));
|
||||
|
||||
j.put("good sized", 9223372036854775807L);
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
a = new JSONArray("[2147483647, 2147483648, 9223372036854775807, 9223372036854775808]");
|
||||
System.out.println(a.toString(4));
|
||||
|
||||
System.out.println("\nKeys: ");
|
||||
it = j.keys();
|
||||
while (it.hasNext()) {
|
||||
s = (String) it.next();
|
||||
System.out.println(s + ": " + j.getString(s));
|
||||
}
|
||||
|
||||
System.out.println("\naccumulate: ");
|
||||
j = new JSONObject();
|
||||
j.accumulate("stooge", "Curly");
|
||||
j.accumulate("stooge", "Larry");
|
||||
j.accumulate("stooge", "Moe");
|
||||
a = j.getJSONArray("stooge");
|
||||
a.put(5, "Shemp");
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
System.out.println("\nwrite:");
|
||||
System.out.println(j.write(new StringWriter()));
|
||||
|
||||
Collection<?> c = null;
|
||||
Map<?, ?> m = null;
|
||||
|
||||
j = new JSONObject(m);
|
||||
a = new JSONArray(c);
|
||||
j.append("stooge", "Joe DeRita");
|
||||
j.append("stooge", "Shemp");
|
||||
j.accumulate("stooges", "Curly");
|
||||
j.accumulate("stooges", "Larry");
|
||||
j.accumulate("stooges", "Moe");
|
||||
j.accumulate("stoogearray", j.get("stooges"));
|
||||
j.put("map", m);
|
||||
j.put("collection", c);
|
||||
j.put("array", a);
|
||||
a.put(m);
|
||||
a.put(c);
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
s =
|
||||
"{plist=Apple; AnimalSmells = { pig = piggish; lamb = lambish; worm = wormy; }; AnimalSounds = { pig = oink; lamb = baa; worm = baa; Lisa = \"Why is the worm talking like a lamb?\" } ; AnimalColors = { pig = pink; lamb = black; worm = pink; } } ";
|
||||
j = new JSONObject(s);
|
||||
System.out.println(j.toString(4));
|
||||
|
||||
s = " (\"San Francisco\", \"New York\", \"Seoul\", \"London\", \"Seattle\", \"Shanghai\")";
|
||||
a = new JSONArray(s);
|
||||
System.out.println(a.toString());
|
||||
|
||||
System.out.println("\nTesting Exceptions: ");
|
||||
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
a = new JSONArray();
|
||||
a.put(Double.NEGATIVE_INFINITY);
|
||||
a.put(Double.NaN);
|
||||
System.out.println(a.toString());
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(j.getDouble("stooge"));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(j.getDouble("howard"));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(j.put(null, "howard"));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(a.getDouble(0));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(a.get(-1));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
System.out.println(a.put(Double.NaN));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
ja = new JSONArray(new Object());
|
||||
System.out.println(ja.toString());
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
s = "[)";
|
||||
a = new JSONArray(s);
|
||||
System.out.println(a.toString());
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
s = "{\"koda\": true, \"koda\": true}";
|
||||
j = new JSONObject(s);
|
||||
System.out.println(j.toString(4));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
|
||||
System.out.print("Exception: ");
|
||||
try {
|
||||
jj = new JSONStringer();
|
||||
s =
|
||||
jj.object()
|
||||
.key("bosanda")
|
||||
.value("MARIE HAA'S")
|
||||
.key("bosanda")
|
||||
.value("MARIE HAA\\'S")
|
||||
.endObject()
|
||||
.toString();
|
||||
System.out.println(j.toString(4));
|
||||
} catch (Exception e) {
|
||||
System.out.println(e);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* OtterTune - AbstractJSONValidationTestCase.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.types.JSONSchemaType;
|
||||
import com.controller.util.FileUtil;
|
||||
import java.io.File;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public abstract class AbstractJSONValidationTestCase extends TestCase {
|
||||
|
||||
private static final String SAMPLE_OUTPUT_PATH = "sample_output";
|
||||
private static final String SAMPLE_CONFIG_PATH = "config";
|
||||
|
||||
protected String dbName;
|
||||
|
||||
protected void setUp(String dbName) throws Exception {
|
||||
super.setUp();
|
||||
this.dbName = dbName;
|
||||
}
|
||||
|
||||
public void testJsonKnobs() {
|
||||
String jsonKnobsPath = FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "knobs.json");
|
||||
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonKnobsPath)));
|
||||
}
|
||||
|
||||
public void testJsonMetrics() {
|
||||
String jsonMetricsBeforePath =
|
||||
FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "metrics_before.json");
|
||||
String jsonMetricsAfterPath =
|
||||
FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "metrics_after.json");
|
||||
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonMetricsBeforePath)));
|
||||
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, new File(jsonMetricsAfterPath)));
|
||||
}
|
||||
|
||||
public void testJsonSummary() {
|
||||
String jsonSummaryPath = FileUtil.joinPath(SAMPLE_OUTPUT_PATH, this.dbName, "summary.json");
|
||||
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.SUMMARY, new File(jsonSummaryPath)));
|
||||
}
|
||||
|
||||
public void testJsonConfig() {
|
||||
String jsonConfigPath =
|
||||
FileUtil.joinPath(SAMPLE_CONFIG_PATH, "sample_" + this.dbName + "_config.json");
|
||||
assertTrue(JSONSchemaType.isValidJson(JSONSchemaType.CONFIG, new File(jsonConfigPath)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* OtterTune - TestInvalidJSON.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
import com.controller.types.JSONSchemaType;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
public class TestInvalidJSON extends TestCase {
|
||||
|
||||
// Wrong number of levels for "global"
|
||||
private static final String BAD_JSON_TEXT_1 =
|
||||
"{"
|
||||
+ " \"global\" : {"
|
||||
+ " \"global\" : {"
|
||||
+ " \"auto_generate_certs\": {"
|
||||
+ " \"auto_pram\" : \"NO\""
|
||||
+ " }"
|
||||
+ " }"
|
||||
+ " },"
|
||||
+ " \"local\" : {"
|
||||
+ " }"
|
||||
+ "}";
|
||||
|
||||
// Lacking "local"
|
||||
private static final String BAD_JSON_TEXT_2 =
|
||||
"{"
|
||||
+ " \"global\" : {"
|
||||
+ " \"global1\" : {"
|
||||
+ " \"auto_generate_certs\": \"ON\""
|
||||
+ " }"
|
||||
+ " }"
|
||||
+ "}";
|
||||
|
||||
public void testBadJSONOutput() {
|
||||
assertFalse(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, BAD_JSON_TEXT_1));
|
||||
assertFalse(JSONSchemaType.isValidJson(JSONSchemaType.OUTPUT, BAD_JSON_TEXT_2));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* OtterTune - TestMySQLJSON.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
public class TestMySQLJSON extends AbstractJSONValidationTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp("mysql");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* OtterTune - TestOracleJSON.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
public class TestOracleJSON extends AbstractJSONValidationTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp("oracle");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* OtterTune - TestPostgresJSON.java
|
||||
*
|
||||
* Copyright (c) 2017-18, Carnegie Mellon University Database Group
|
||||
*/
|
||||
|
||||
package com.controller.collectors;
|
||||
|
||||
public class TestPostgresJSON extends AbstractJSONValidationTestCase {
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp("postgres");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user