/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.util;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A helper class for <a href="http://json-schema.org/">JSON schema</a>.
*/
public final class JsonSchemaHelper {
// 0 = text, 1 = enum, 2 = boolean, 3 = integer or number
private static final Pattern PATTERN = Pattern.compile("\"(.+?)\"|\\[(.+)\\]|(true|false)|(-?\\d+\\.?\\d*)");
private static final String QUOT = """;
private JsonSchemaHelper() {
}
/**
* Gets the JSon schema type.
*
* @param type the java type
* @return the json schema type, is never null, but returns <tt>object</tt> as the generic type
*/
public static String getType(Class<?> type) {
if (type.isEnum()) {
return "enum";
} else if (type.isArray()) {
return "array";
}
if (type.isAssignableFrom(URI.class) || type.isAssignableFrom(URL.class)) {
return "string";
}
String primitive = getPrimitiveType(type.getCanonicalName());
if (primitive != null) {
return primitive;
}
return "object";
}
/**
* Gets the JSon schema primitive type.
*
* @param name the java type
* @return the json schema primitive type, or <tt>null</tt> if not a primitive
*/
public static String getPrimitiveType(String name) {
// special for byte[] or Object[] as its common to use
if ("java.lang.byte[]".equals(name) || "byte[]".equals(name)) {
return "string";
} else if ("java.lang.Byte[]".equals(name) || "Byte[]".equals(name)) {
return "array";
} else if ("java.lang.Object[]".equals(name) || "Object[]".equals(name)) {
return "array";
} else if ("java.lang.String[]".equals(name) || "String[]".equals(name)) {
return "array";
} else if ("java.lang.Character".equals(name) || "Character".equals(name) || "char".equals(name)) {
return "string";
} else if ("java.lang.String".equals(name) || "String".equals(name)) {
return "string";
} else if ("java.lang.Boolean".equals(name) || "Boolean".equals(name) || "boolean".equals(name)) {
return "boolean";
} else if ("java.lang.Integer".equals(name) || "Integer".equals(name) || "int".equals(name)) {
return "integer";
} else if ("java.lang.Long".equals(name) || "Long".equals(name) || "long".equals(name)) {
return "integer";
} else if ("java.lang.Short".equals(name) || "Short".equals(name) || "short".equals(name)) {
return "integer";
} else if ("java.lang.Byte".equals(name) || "Byte".equals(name) || "byte".equals(name)) {
return "integer";
} else if ("java.lang.Float".equals(name) || "Float".equals(name) || "float".equals(name)) {
return "number";
} else if ("java.lang.Double".equals(name) || "Double".equals(name) || "double".equals(name)) {
return "number";
}
return null;
}
/**
* Parses the json schema to split it into a list or rows, where each row contains key value pairs with the metadata
*
* @param group the group to parse from such as <tt>component</tt>, <tt>componentProperties</tt>, or <tt>properties</tt>.
* @param json the json
* @return a list of all the rows, where each row is a set of key value pairs with metadata
*/
public static List<Map<String, String>> parseJsonSchema(String group, String json, boolean parseProperties) {
List<Map<String, String>> answer = new ArrayList<Map<String, String>>();
if (json == null) {
return answer;
}
boolean found = false;
// parse line by line
String[] lines = json.split("\n");
for (String line : lines) {
// we need to find the group first
if (!found) {
String s = line.trim();
found = s.startsWith("\"" + group + "\":") && s.endsWith("{");
continue;
}
// we should stop when we end the group
if (line.equals(" },") || line.equals(" }")) {
break;
}
// need to safe encode \" so we can parse the line
line = line.replaceAll("\"\\\\\"\"", '"' + QUOT + '"');
Map<String, String> row = new LinkedHashMap<String, String>();
Matcher matcher = PATTERN.matcher(line);
String key;
if (parseProperties) {
// when parsing properties the first key is given as name, so the first parsed token is the value of the name
key = "name";
} else {
key = null;
}
while (matcher.find()) {
if (key == null) {
key = matcher.group(1);
} else {
String value = matcher.group(1);
if (value != null) {
// its text based
value = value.trim();
// decode
value = value.replaceAll(QUOT, "\"");
value = decodeJson(value);
}
if (value == null) {
// not text then its maybe an enum?
value = matcher.group(2);
if (value != null) {
// its an enum so strip out " and trim spaces after comma
value = value.replaceAll("\"", "");
value = value.replaceAll(", ", ",");
value = value.trim();
}
}
if (value == null) {
// not text then its maybe a boolean?
value = matcher.group(3);
}
if (value == null) {
// not text then its maybe a integer?
value = matcher.group(4);
}
if (value != null) {
row.put(key, value);
}
// reset
key = null;
}
}
if (!row.isEmpty()) {
answer.add(row);
}
}
return answer;
}
private static String decodeJson(String value) {
// json encodes a \ as \\ so we need to decode from \\ back to \
if ("\\\\".equals(value)) {
value = "\\";
}
return value;
}
/**
* Is the property required
*
* @param rows the rows of properties
* @param name name of the property
* @return <tt>true</tt> if required, or <tt>false</tt> if not
*/
public static boolean isPropertyRequired(List<Map<String, String>> rows, String name) {
for (Map<String, String> row : rows) {
boolean required = false;
boolean found = false;
if (row.containsKey("name")) {
found = name.equals(row.get("name"));
}
if (row.containsKey("required")) {
required = "true".equals(row.get("required"));
}
if (found) {
return required;
}
}
return false;
}
/**
* Gets the default value of the property
*
* @param rows the rows of properties
* @param name name of the property
* @return the default value or <tt>null</tt> if no default value exists
*/
public static String getPropertyDefaultValue(List<Map<String, String>> rows, String name) {
for (Map<String, String> row : rows) {
String defaultValue = null;
boolean found = false;
if (row.containsKey("name")) {
found = name.equals(row.get("name"));
}
if (row.containsKey("defaultValue")) {
defaultValue = row.get("defaultValue");
}
if (found) {
return defaultValue;
}
}
return null;
}
/**
* Is the property multi valued
*
* @param rows the rows of properties
* @param name name of the property
* @return <tt>true</tt> if multi valued, or <tt>false</tt> if not
*/
public static boolean isPropertyMultiValue(List<Map<String, String>> rows, String name) {
for (Map<String, String> row : rows) {
boolean multiValue = false;
boolean found = false;
if (row.containsKey("name")) {
found = name.equals(row.get("name"));
}
if (row.containsKey("multiValue")) {
multiValue = "true".equals(row.get("multiValue"));
}
if (found) {
return multiValue;
}
}
return false;
}
/**
* Gets the prefix value of the property
*
* @param rows the rows of properties
* @param name name of the property
* @return the prefix value or <tt>null</tt> if no prefix value exists
*/
public static String getPropertyPrefix(List<Map<String, String>> rows, String name) {
for (Map<String, String> row : rows) {
String prefix = null;
boolean found = false;
if (row.containsKey("name")) {
found = name.equals(row.get("name"));
}
if (row.containsKey("prefix")) {
prefix = row.get("prefix");
}
if (found) {
return prefix;
}
}
return null;
}
}