/*
SwaggerJsonParser.java
Copyright (c) 2016 NTT DOCOMO,INC.
Released under the MIT license
http://opensource.org/licenses/mit-license.php
*/
package org.deviceconnect.android.profile.spec.parser;
import android.os.Bundle;
import org.deviceconnect.android.profile.spec.ArrayDataSpec;
import org.deviceconnect.android.profile.spec.ArrayParameterSpec;
import org.deviceconnect.android.profile.spec.BooleanDataSpec;
import org.deviceconnect.android.profile.spec.BooleanParameterSpec;
import org.deviceconnect.android.profile.spec.DConnectApiSpec;
import org.deviceconnect.android.profile.spec.DConnectDataSpec;
import org.deviceconnect.android.profile.spec.DConnectParameterSpec;
import org.deviceconnect.android.profile.spec.DConnectProfileSpec;
import org.deviceconnect.android.profile.spec.DConnectSpecConstants;
import org.deviceconnect.android.profile.spec.FileParameterSpec;
import org.deviceconnect.android.profile.spec.IntegerDataSpec;
import org.deviceconnect.android.profile.spec.IntegerParameterSpec;
import org.deviceconnect.android.profile.spec.NumberDataSpec;
import org.deviceconnect.android.profile.spec.NumberParameterSpec;
import org.deviceconnect.android.profile.spec.StringDataSpec;
import org.deviceconnect.android.profile.spec.StringParameterSpec;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
class SwaggerJsonParser implements DConnectProfileSpecJsonParser, DConnectSpecConstants {
private static final String KEY_BASE_PATH = "basePath";
private static final String KEY_PATHS = "paths";
private static final OperationObjectParser OPERATION_OBJECT_PARSER = new OperationObjectParser() {
@Override
public DConnectApiSpec parseJson(final Method method, final JSONObject opObj) throws JSONException {
DConnectSpecConstants.Type type
= DConnectSpecConstants.Type.parse(opObj.getString(KEY_X_TYPE));
JSONArray parameters = opObj.getJSONArray(KEY_PARAMETERS);
List<DConnectParameterSpec> paramSpecList = new ArrayList<DConnectParameterSpec>();
for (int i = 0; i < parameters.length(); i++) {
JSONObject paramObj = parameters.getJSONObject(i);
ParameterObjectParser parser = getParameterParser(paramObj);
DConnectParameterSpec paramSpec = parser.parseJson(paramObj);
paramSpecList.add(paramSpec);
}
return new DConnectApiSpec.Builder()
.setType(type)
.setMethod(method)
.setRequestParamList(paramSpecList)
.build();
}
};
private static final ItemsObjectParser ARRAY_ITEMS_PARSER = new ArrayItemsObjectParser();
private static final ItemsObjectParser BOOLEAN_ITEMS_PARSER = new BooleanItemsObjectParser();
private static final ItemsObjectParser INTEGER_ITEMS_PARSER = new IntegerItemsObjectParser();
private static final ItemsObjectParser NUMBER_ITEMS_PARSER = new NumberItemsObjectParser();
private static final ItemsObjectParser STRING_ITEMS_PARSER = new StringItemsObjectParser();
private static final ParameterObjectParser ARRAY_PARAM_PARSER = new ArrayParameterParser();
private static final ParameterObjectParser BOOLEAN_PARAM_PARSER = new BooleanParameterParser();
private static final ParameterObjectParser FILE_PARAM_PARSER = new FileParameterParser();
private static final ParameterObjectParser INTEGER_PARAM_PARSER = new IntegerParameterParser();
private static final ParameterObjectParser NUMBER_PARAM_PARSER = new NumberParameterParser();
private static final ParameterObjectParser STRING_PARAM_PARSER = new StringParameterParser();
@Override
public DConnectProfileSpec parseJson(final JSONObject json) throws JSONException {
DConnectProfileSpec.Builder builder = new DConnectProfileSpec.Builder();
builder.setBundle(toBundle(json));
String basePath = json.optString(KEY_BASE_PATH, null);
if (basePath != null) {
String[] parts = basePath.split("/");
if (parts.length != 3) {
throw new JSONException("basePath is invalid: " + basePath);
}
builder.setApiName(parts[1]);
builder.setProfileName(parts[2]);
}
JSONObject pathsObj = json.getJSONObject(KEY_PATHS);
for (Iterator<String> it = pathsObj.keys(); it.hasNext(); ) {
String path = it.next();
JSONObject pathObj = pathsObj.getJSONObject(path);
for (DConnectSpecConstants.Method method : DConnectSpecConstants.Method.values()) {
JSONObject opObj = pathObj.optJSONObject(method.getName().toLowerCase());
if (opObj == null) {
continue;
}
DConnectApiSpec apiSpec = OPERATION_OBJECT_PARSER.parseJson(method, opObj);
if (apiSpec != null) {
builder.addApiSpec(path, method, apiSpec);
}
}
}
return builder.build();
}
private Bundle toBundle(final JSONObject jsonObj) throws JSONException {
Bundle bundle = new Bundle();
for (Iterator<String> it = jsonObj.keys(); it.hasNext(); ) {
String name = it.next();
Object value = jsonObj.get(name);
if (value instanceof JSONArray) {
putArray(bundle, name, (JSONArray) value);
} else if (value instanceof JSONObject) {
bundle.putBundle(name, toBundle((JSONObject) value));
} else if (value instanceof Serializable) {
bundle.putSerializable(name, (Serializable) value);
}
}
return bundle;
}
private void putArray(final Bundle bundle, final String name, final JSONArray jsonArray)
throws JSONException {
if (jsonArray.length() == 0) {
bundle.putParcelableArray(name, new Bundle[0]);
} else {
final Class base = getBaseClass(jsonArray);
final int length = jsonArray.length();
if (base == Integer.class) {
int[] array = new int[length];
for (int i = 0; i < length; i++) {
array[i] = jsonArray.getInt(i);
}
bundle.putIntArray(name, array);
} else if (base == Long.class) {
long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = jsonArray.getLong(i);
}
bundle.putLongArray(name, array);
} else if (base == Double.class) {
double[] array = new double[length];
for (int i = 0; i < length; i++) {
array[i] = jsonArray.getDouble(i);
}
bundle.putDoubleArray(name, array);
} else if (base == String.class) {
String[] array = new String[length];
for (int i = 0; i < length; i++) {
array[i] = jsonArray.getString(i);
}
bundle.putStringArray(name, array);
} else if (base == Boolean.class) {
boolean[] array = new boolean[length];
for (int i = 0; i < length; i++) {
array[i] = jsonArray.getBoolean(i);
}
bundle.putBooleanArray(name, array);
} else if (base == JSONObject.class) {
Bundle[] array = new Bundle[length];
for (int i = 0; i < length; i++) {
array[i] = toBundle(jsonArray.getJSONObject(i));
}
bundle.putParcelableArray(name, array);
}
}
}
private Class getBaseClass(final JSONArray array) throws JSONException {
Class cls = array.get(0).getClass();
for (int i = 1; i < array.length(); i++) {
if (cls != array.get(i).getClass()) {
return null;
}
}
return cls;
}
private static ParameterObjectParser getParameterParser(final JSONObject json) throws JSONException {
String type = json.getString(ParameterObjectParser.KEY_TYPE);
DataType paramType = DataType.fromName(type);
if (paramType == null) {
throw new JSONException("Unknown parameter type '" + type + "' is specified.");
}
switch (paramType) {
case BOOLEAN:
return BOOLEAN_PARAM_PARSER;
case INTEGER:
return INTEGER_PARAM_PARSER;
case NUMBER:
return NUMBER_PARAM_PARSER;
case STRING:
return STRING_PARAM_PARSER;
case FILE:
return FILE_PARAM_PARSER;
case ARRAY:
return ARRAY_PARAM_PARSER;
default:
throw new IllegalArgumentException();
}
}
private static ParameterObjectParser getItemParser(final JSONObject json) throws JSONException {
String type = json.getString(ParameterObjectParser.KEY_TYPE);
DataType paramType = DataType.fromName(type);
if (paramType == null) {
throw new JSONException("Unknown parameter type '" + type + "' is specified.");
}
switch (paramType) {
case BOOLEAN:
return BOOLEAN_PARAM_PARSER;
case INTEGER:
return INTEGER_PARAM_PARSER;
case NUMBER:
return NUMBER_PARAM_PARSER;
case STRING:
return STRING_PARAM_PARSER;
case FILE:
return FILE_PARAM_PARSER;
case ARRAY:
return ARRAY_PARAM_PARSER;
default:
throw new IllegalArgumentException();
}
}
private interface OperationObjectParser {
String KEY_X_TYPE = "x-type";
String KEY_PARAMETERS = "parameters";
DConnectApiSpec parseJson(Method method, JSONObject json) throws JSONException;
}
private interface ParameterObjectParser {
String KEY_NAME = "name";
String KEY_REQUIRED = "required";
String KEY_TYPE = "type";
DConnectParameterSpec parseJson(JSONObject json) throws JSONException;
}
private static class ArrayParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
ArrayDataSpec dataSpec = (ArrayDataSpec) ARRAY_ITEMS_PARSER.parseJson(json);
ArrayParameterSpec.Builder builder = new ArrayParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
if (json.has(KEY_REQUIRED)) {
builder.setRequired(json.getBoolean(KEY_REQUIRED));
}
builder.setItemsSpec(dataSpec.getItemsSpec());
builder.setMaxLength(dataSpec.getMaxLength());
builder.setMinLength(dataSpec.getMinLength());
return builder.build();
}
}
private static class BooleanParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
BooleanParameterSpec.Builder builder = new BooleanParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
if (json.has(KEY_REQUIRED)) {
builder.setRequired(json.getBoolean(KEY_REQUIRED));
}
return builder.build();
}
}
private static class IntegerParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
IntegerDataSpec dataSpec = (IntegerDataSpec) INTEGER_ITEMS_PARSER.parseJson(json);
IntegerParameterSpec.Builder builder = new IntegerParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
if (json.has(KEY_REQUIRED)) {
builder.setRequired(json.getBoolean(KEY_REQUIRED));
}
builder.setFormat(dataSpec.getFormat());
builder.setMaximum(dataSpec.getMaximum());
builder.setMinimum(dataSpec.getMinimum());
builder.setExclusiveMaximum(dataSpec.isExclusiveMaximum());
builder.setExclusiveMinimum(dataSpec.isExclusiveMinimum());
builder.setEnumList(dataSpec.getEnumList());
return builder.build();
}
}
private static class NumberParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
NumberDataSpec dataSpec = (NumberDataSpec) NUMBER_ITEMS_PARSER.parseJson(json);
NumberParameterSpec.Builder builder = new NumberParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
if (json.has(KEY_REQUIRED)) {
builder.setRequired(json.getBoolean(KEY_REQUIRED));
}
builder.setFormat(dataSpec.getFormat());
builder.setMaximum(dataSpec.getMaximum());
builder.setMinimum(dataSpec.getMinimum());
builder.setExclusiveMaximum(dataSpec.isExclusiveMaximum());
builder.setExclusiveMinimum(dataSpec.isExclusiveMinimum());
return builder.build();
}
}
private static class StringParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
StringDataSpec dataSpec = (StringDataSpec) STRING_ITEMS_PARSER.parseJson(json);
StringParameterSpec.Builder builder = new StringParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
if (json.has(KEY_REQUIRED)) {
builder.setRequired(json.getBoolean(KEY_REQUIRED));
}
builder.setFormat(dataSpec.getFormat());
builder.setMaxLength(dataSpec.getMaxLength());
builder.setMinLength(dataSpec.getMinLength());
builder.setEnumList(dataSpec.getEnumList());
return builder.build();
}
}
private static class FileParameterParser implements ParameterObjectParser {
@Override
public DConnectParameterSpec parseJson(final JSONObject json) throws JSONException {
FileParameterSpec.Builder builder = new FileParameterSpec.Builder();
builder.setName(json.getString(KEY_NAME));
builder.setRequired(json.getBoolean(KEY_REQUIRED));
return builder.build();
}
}
private interface ItemsObjectParser {
String KEY_FORMAT = "format";
String KEY_MAXIMUM = "maximum";
String KEY_MINIMUM = "minimum";
String KEY_EXCLUSIVE_MAXIMUM = "exclusiveMaximum";
String KEY_EXCLUSIVE_MINIMUM = "exclusiveMinimum";
String KEY_MAX_LENGTH = "maxLength";
String KEY_MIN_LENGTH = "minLength";
String KEY_ENUM = "enum";
String KEY_ITEMS = "items";
DConnectDataSpec parseJson(JSONObject json) throws JSONException;
}
private static class ArrayItemsObjectParser implements ItemsObjectParser {
@Override
public DConnectDataSpec parseJson(final JSONObject json) throws JSONException {
ArrayDataSpec.Builder builder = new ArrayDataSpec.Builder();
JSONObject itemsObj = json.getJSONObject(KEY_ITEMS);
ItemsObjectParser parser = getItemsParser(itemsObj);
DConnectDataSpec itemSpec = parser.parseJson(itemsObj);
builder.setItemsSpec(itemSpec);
if (json.has(KEY_MAX_LENGTH)) {
builder.setMaxLength(json.getInt(KEY_MAX_LENGTH));
}
if (json.has(KEY_MIN_LENGTH)) {
builder.setMinLength(json.getInt(KEY_MIN_LENGTH));
}
return builder.build();
}
public ItemsObjectParser getItemsParser(final JSONObject json) throws JSONException {
String type = json.getString(ParameterObjectParser.KEY_TYPE);
DataType paramType = DataType.fromName(type);
if (paramType == null) {
throw new JSONException("Unknown parameter type '" + type + "' is specified.");
}
switch (paramType) {
case BOOLEAN:
return BOOLEAN_ITEMS_PARSER;
case INTEGER:
return INTEGER_ITEMS_PARSER;
case NUMBER:
return NUMBER_ITEMS_PARSER;
case STRING:
return STRING_ITEMS_PARSER;
case ARRAY:
return this;
default:
throw new IllegalArgumentException();
}
}
}
private static class BooleanItemsObjectParser implements ItemsObjectParser {
@Override
public DConnectDataSpec parseJson(final JSONObject json) throws JSONException {
return new BooleanDataSpec.Builder().build();
}
}
private static class IntegerItemsObjectParser implements ItemsObjectParser {
@Override
public DConnectDataSpec parseJson(final JSONObject json) throws JSONException {
IntegerDataSpec.Builder builder = new IntegerDataSpec.Builder();
if (json.has(KEY_FORMAT)) {
DataFormat format = DataFormat.fromName(json.optString(KEY_FORMAT));
if (format == null) {
throw new IllegalArgumentException("format is invalid: " + json.optString(KEY_FORMAT));
}
builder.setFormat(format);
}
if (json.has(KEY_MAXIMUM)) {
builder.setMaximum(json.getLong(KEY_MAXIMUM));
}
if (json.has(KEY_MINIMUM)) {
builder.setMinimum(json.getLong(KEY_MINIMUM));
}
if (json.has(KEY_EXCLUSIVE_MAXIMUM)) {
builder.setExclusiveMaximum(json.getBoolean(KEY_EXCLUSIVE_MAXIMUM));
}
if (json.has(KEY_EXCLUSIVE_MINIMUM)) {
builder.setExclusiveMinimum(json.getBoolean(KEY_EXCLUSIVE_MINIMUM));
}
if (json.has(KEY_ENUM)) {
JSONArray array = json.getJSONArray(KEY_ENUM);
long[] enumList = new long[array.length()];
for (int i = 0; i < array.length(); i++) {
enumList[i] = array.getLong(i);
}
builder.setEnumList(enumList);
}
return builder.build();
}
}
private static class NumberItemsObjectParser implements ItemsObjectParser {
@Override
public DConnectDataSpec parseJson(final JSONObject json) throws JSONException {
NumberDataSpec.Builder builder = new NumberDataSpec.Builder();
if (json.has(KEY_FORMAT)) {
DataFormat format = DataFormat.fromName(json.optString(KEY_FORMAT));
if (format == null) {
throw new IllegalArgumentException("format is invalid: " + json.optString(KEY_FORMAT));
}
builder.setFormat(format);
}
if (json.has(KEY_MAXIMUM)) {
builder.setMaximum(json.getDouble(KEY_MAXIMUM));
}
if (json.has(KEY_MINIMUM)) {
builder.setMinimum(json.getDouble(KEY_MINIMUM));
}
if (json.has(KEY_EXCLUSIVE_MAXIMUM)) {
builder.setExclusiveMaximum(json.getBoolean(KEY_EXCLUSIVE_MAXIMUM));
}
if (json.has(KEY_EXCLUSIVE_MINIMUM)) {
builder.setExclusiveMinimum(json.getBoolean(KEY_EXCLUSIVE_MINIMUM));
}
return builder.build();
}
}
private static class StringItemsObjectParser implements ItemsObjectParser {
@Override
public DConnectDataSpec parseJson(final JSONObject json) throws JSONException {
StringDataSpec.Builder builder = new StringDataSpec.Builder();
if (json.has(KEY_FORMAT)) {
DataFormat format = DataFormat.fromName(json.getString(KEY_FORMAT));
if (format == null) {
throw new IllegalArgumentException("format is invalid: " + json.getString(KEY_FORMAT));
}
builder.setFormat(format);
}
if (json.has(KEY_MAX_LENGTH)) {
builder.setMaxLength(json.getInt(KEY_MAX_LENGTH));
}
if (json.has(KEY_MIN_LENGTH)) {
builder.setMinLength(json.getInt(KEY_MIN_LENGTH));
}
if (json.has(KEY_ENUM)) {
JSONArray array = json.getJSONArray(KEY_ENUM);
String[] enumList = new String[array.length()];
for (int i = 0; i < array.length(); i++) {
enumList[i] = array.getString(i);
}
builder.setEnumList(enumList);
}
return builder.build();
}
}
}