# JsonProcessor.java

    /*
     * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     * WSO2 Inc. 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.synapse.commons.json.jsonprocessor.parser;

    import com.google.gson.JsonElement;
    import com.google.gson.JsonNull;
    import com.google.gson.JsonObject;
    import com.google.gson.JsonParser;
    import com.google.gson.JsonSyntaxException;
    import org.apache.commons.lang.StringUtils;
    import org.apache.synapse.commons.json.jsonprocessor.constants.ValidatorConstants;
    import org.apache.synapse.commons.json.jsonprocessor.exceptions.ParserException;
    import org.apache.synapse.commons.json.jsonprocessor.exceptions.ValidatorException;
    import org.apache.synapse.commons.json.jsonprocessor.utils.GSONDataTypeConverter;
    import org.apache.synapse.commons.json.jsonprocessor.utils.JsonProcessorUtils;
    import org.apache.synapse.commons.json.jsonprocessor.validators.ArrayValidator;
    import org.apache.synapse.commons.json.jsonprocessor.validators.BooleanValidator;
    import org.apache.synapse.commons.json.jsonprocessor.validators.NullValidator;
    import org.apache.synapse.commons.json.jsonprocessor.validators.NumericValidator;
    import org.apache.synapse.commons.json.jsonprocessor.validators.ObjectValidator;
    import org.apache.synapse.commons.json.jsonprocessor.validators.StringValidator;

    /**
     * This class will parse a given JSON input according to a given schema.
     * Supported inout formats - String and Gson JsonObject
     */
    public class JsonProcessor {

        // JSON parser instance
        private static JsonParser parser = new JsonParser();

        // Use without instantiating
        private JsonProcessor() {

        }

        /**
         * This method parse a given JSON string according to the given schema. Both as string.
         *
         * @param inputString input String.
         * @param inputSchema input Schema.
         * @return corrected String.
         * @throws ValidatorException Exception occurs in validation process.
         * @throws ParserException    Exception occurs in data type parsing.
         */
        public static String parseJson(String inputString, String inputSchema) throws ValidatorException, ParserException {
            if (StringUtils.isNotEmpty(inputString) && StringUtils.isNotEmpty(inputSchema)) {
                JsonObject schemaObject;
                JsonElement schema;
                try {
                    schema = parser.parse(inputSchema);
                } catch (JsonSyntaxException ex) {
                    throw new ValidatorException("Invalid JSON schema", ex);
                }
                if (schema.isJsonObject()) {
                    // Handling empty JSON objects - valid for all inputs
                    if (schema.toString().replaceAll("\\s+","").equals("{}")) {
                        return inputString;
                    }
                    schemaObject = schema.getAsJsonObject();
                } else if (schema.isJsonPrimitive()) {
                    // if schema is primitive it should be a boolean
                    boolean valid = schema.getAsBoolean();
                    if (valid) {
                        return inputString;
                    } else {
                        throw new ValidatorException("JSON schema is false, so all validations will fail");
                    }
                } else {
                    throw new ValidatorException("JSON schema should be an object or boolean");
                }
                return parseJson(inputString, schemaObject);
            } else {
                throw new ParserException("Input json and schema should not be null");
            }
        }

        /**
         * This method will parse a given JSON string according to the given schema. Schema as an Object.
         * Can use this method when using caching.
         *
         * @param inputString input JSON string.
         * @param schema      already parsed JSON schema.
         * @return corrected JSON string.
         * @throws ValidatorException Exception occurs in validation process.
         * @throws ParserException    Exception occurs in data type parsing.
         */
        private static String parseJson(String inputString, Object schema) throws ValidatorException, ParserException {
            if (StringUtils.isNotEmpty(inputString) && schema instanceof JsonObject) {
                JsonElement result = null;
                JsonObject schemaObject = (JsonObject) schema;
                if (((JsonObject) schema).has(ValidatorConstants.TYPE_KEY)) {
                    String type = JsonProcessorUtils.replaceEnclosingQuotes(
                            schemaObject.get(ValidatorConstants.TYPE_KEY).toString());
                    if (ValidatorConstants.BOOLEAN_KEYS.contains(type)) {
                        result = BooleanValidator.validateBoolean(schemaObject, inputString);
                    } else if (ValidatorConstants.NOMINAL_KEYS.contains(type)) {
                        result = StringValidator.validateNominal(schemaObject, inputString);
                    } else if (ValidatorConstants.NUMERIC_KEYS.contains(type)) {
                        result = NumericValidator.validateNumeric(schemaObject, inputString);
                    } else if (ValidatorConstants.ARRAY_KEYS.contains(type)) {
                        result = ArrayValidator.validateArray(GSONDataTypeConverter.getMapFromString(inputString),
                                schemaObject);
                    } else if (ValidatorConstants.NULL_KEYS.contains(type)) {
                        NullValidator.validateNull(schemaObject, inputString);
                        result = JsonNull.INSTANCE;
                    } else if (ValidatorConstants.OBJECT_KEYS.contains(type)) {
                        JsonElement input = parser.parse(inputString);
                        if (input.isJsonObject()) {
                            result = ObjectValidator.validateObject(input.getAsJsonObject(), schemaObject);
                        } else {
                            throw new ValidatorException(
                                    "Expected a JSON as input but found : " + inputString);
                        }
                    }
                    if (result != null) {
                        return result.toString();
                    }
                    return null;
                } else {
                    throw new ValidatorException("JSON schema should contain a type declaration");
                }
            } else {
                throw new ParserException("Input json and schema should not be null, " +
                        "schema should be a JSON object");
            }
        }
    }