All Downloads are FREE. Search and download functionalities are using the official Maven repository.

guru.nidi.ramltester.core.ParameterChecker Maven / Gradle / Ivy

/*
 * Copyright (C) 2014 Stefan Niederhauser ([email protected])
 *
 * Licensed 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 guru.nidi.ramltester.core;

import guru.nidi.ramltester.model.Values;
import guru.nidi.ramltester.util.FileValue;
import org.raml.model.ParamType;
import org.raml.model.parameter.AbstractParam;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

/**
 *
 */
class ParameterChecker {
    private static final Pattern INTEGER = Pattern.compile("0|-?[1-9][0-9]*");
    private static final Pattern NUMBER = Pattern.compile("0|inf|-inf|nan|-?(((0?|[1-9][0-9]*)\\.[0-9]*[1-9])|([1-9][0-9]*))(e[-+]?[1-9][0-9]*)?");
    private static final String DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss 'GMT'";

    private final Logger log = LoggerFactory.getLogger(getClass());

    private final RamlViolations violations;
    private final boolean acceptUndefined;
    private final boolean acceptWildcard;
    private final boolean ignoreX;
    private final Set predefined;

    ParameterChecker(RamlViolations violations, boolean acceptUndefined, boolean acceptWildcard, boolean ignoreX, Set predefined) {
        this.violations = violations;
        this.acceptUndefined = acceptUndefined;
        this.acceptWildcard = acceptWildcard;
        this.ignoreX = ignoreX;
        this.predefined = predefined;
    }

    ParameterChecker(RamlViolations violations) {
        this(violations, false, false, false, Collections.emptySet());
    }

    ParameterChecker acceptUndefined() {
        return new ParameterChecker(violations, true, acceptWildcard, ignoreX, predefined);
    }

    ParameterChecker acceptWildcard() {
        return new ParameterChecker(violations, acceptUndefined, true, ignoreX, predefined);
    }

    ParameterChecker ignoreX(boolean ignoreX) {
        return new ParameterChecker(violations, acceptUndefined, acceptWildcard, ignoreX, predefined);
    }

    ParameterChecker predefined(Set predefined) {
        return new ParameterChecker(violations, acceptUndefined, acceptWildcard, ignoreX, predefined);
    }

    public Set checkParameters(Map params, Values values, Message message) {
        Map> listParams = new HashMap<>();
        for (Map.Entry entry : params.entrySet()) {
            listParams.put(entry.getKey(), Collections.singletonList(entry.getValue()));
        }
        return checkListParameters(listParams, values, message);
    }

    private boolean acceptUndefined(String name) {
        return acceptUndefined || predefined.contains(name) || (ignoreX && name.startsWith("x-"));
    }

    public Set checkListParameters(Map> params, Values values, Message message) {
        Set found = new HashSet<>();
        for (Map.Entry> entry : values) {
            final Message namedMsg = message.withParam(entry.getKey());
            final String paramName = findMatchingParamName(params.keySet(), entry.getKey());
            final List parameters = params.get(paramName);
            if (parameters == null || parameters.isEmpty()) {
                violations.addIf(!acceptUndefined(entry.getKey().toLowerCase()), namedMsg.withMessageParam("undefined"));
            } else {
                for (AbstractParam parameter : parameters) {
                    violations.addIf(!parameter.isRepeat() && entry.getValue().size() > 1, namedMsg.withMessageParam("repeat.superfluous"));
                    for (Object value : entry.getValue()) {
                        checkParameter(parameter, value, namedMsg);
                    }
                }
                found.add(paramName);
            }
        }
        for (Map.Entry> entry : params.entrySet()) {
            final Message namedMsg = message.withParam(entry.getKey());
            for (AbstractParam parameter : entry.getValue()) {
                violations.addIf(parameter.isRequired() && !found.contains(entry.getKey()), namedMsg.withMessageParam("required.missing"));
            }
        }
        return found;
    }

    private String findMatchingParamName(Collection paramNames, String name) {
        if (!acceptWildcard) {
            return name;
        }
        for (String key : paramNames) {
            int pos = key.indexOf("{?}");
            if (pos >= 0) {
                if ((pos == 0 || name.startsWith(key.substring(0, pos))) &&
                        (pos == key.length() - 3 || name.endsWith(key.substring(pos + 3)))) {
                    return key;
                }
            } else if (key.equals(name)) {
                return key;
            }
        }
        return null;
    }

    public void checkParameter(AbstractParam param, Object value, Message message) {
        if (value == null) {
            Message detail = message.withInnerParam(new Message("value", "empty"));
            checkNullParameter(param, detail);
        } else {
            Message detail = message.withInnerParam(new Message("value", value));
            if (value instanceof String) {
                checkStringParameter(param, (String) value, detail);
            } else if (value instanceof FileValue) {
                checkFileParameter(param, (FileValue) value, detail);
            } else {
                throw new IllegalArgumentException("Unhandled parameter value '" + value + "' of type " + value.getClass());
            }
        }
    }

    private void checkNullParameter(AbstractParam param, Message detail) {
        if (param.getType() != ParamType.STRING) {
            violations.add(detail.withMessageParam("value.empty"));
        } else {
            checkStringParameter(param, "", detail);
        }
    }

    private void checkFileParameter(AbstractParam param, FileValue value, Message detail) {
        if (param.getType() != ParamType.FILE) {
            violations.add(detail.withMessageParam("file.superfluous", param.getType()));
        }
    }

    private void checkStringParameter(AbstractParam param, String value, Message detail) {
        switch (param.getType()) {
            case BOOLEAN:
                violations.addIf(!value.equals("true") && !value.equals("false"), detail.withMessageParam("boolean.invalid"));
                break;
            case DATE:
                try {
                    final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.ENGLISH);
                    dateFormat.setLenient(false);
                    dateFormat.parse(value);
                } catch (ParseException e) {
                    violations.add(detail.withMessageParam("date.invalid"));
                }
                break;
            case FILE:
                violations.add(detail.withMessageParam("file.invalid"));
                break;
            case INTEGER:
                if (INTEGER.matcher(value).matches()) {
                    checkNumericLimits(param, new BigDecimal(value), detail);
                } else {
                    violations.add(detail.withMessageParam("integer.invalid"));
                }
                break;
            case NUMBER:
                if (NUMBER.matcher(value).matches()) {
                    if ((value.equals("inf") || value.equals("-inf") || value.equals("nan"))) {
                        violations.addIf(param.getMinimum() != null || param.getMaximum() != null, detail.withMessageParam("unbound"));
                    } else {
                        checkNumericLimits(param, new BigDecimal(value), detail);
                    }
                } else {
                    violations.add(detail.withMessageParam("number.invalid"));
                }
                break;
            case STRING:
                violations.addIf(param.getEnumeration() != null && !param.getEnumeration().contains(value),
                        detail.withMessageParam("enum.invalid", param.getEnumeration()));
                try {
                    violations.addIf(param.getPattern() != null && !JsRegex.matches(value, param.getPattern()),
                            detail.withMessageParam("pattern.invalid", param.getPattern()));
                } catch (PatternSyntaxException e) {
                    log.warn("Could not execute regex '" + param.getPattern(), e);
                }
                violations.addIf(param.getMinLength() != null && value.length() < param.getMinLength(),
                        detail.withMessageParam("length.tooSmall", param.getMinLength()));
                violations.addIf(param.getMaxLength() != null && value.length() > param.getMaxLength(),
                        detail.withMessageParam("length.tooBig", param.getMaxLength()));
                break;
        }
    }

    private void checkNumericLimits(AbstractParam param, BigDecimal value, Message message) {
        violations.addIf(param.getMinimum() != null && param.getMinimum().compareTo(value) > 0,
                message.withMessageParam("value.tooSmall", param.getMinimum()));
        violations.addIf(param.getMaximum() != null && param.getMaximum().compareTo(value) < 0,
                message.withMessageParam("value.tooBig", param.getMaximum()));
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy