
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 guru.nidi.ramltester.util.Message;
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 static final String WILDCARD = "{?}";
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 boolean caseSensitive;
private final Set predefined;
ParameterChecker(RamlViolations violations, boolean acceptUndefined, boolean acceptWildcard, boolean ignoreX, boolean caseSensitive, Set predefined) {
this.violations = violations;
this.acceptUndefined = acceptUndefined;
this.acceptWildcard = acceptWildcard;
this.ignoreX = ignoreX;
this.caseSensitive = caseSensitive;
this.predefined = predefined;
}
ParameterChecker(RamlViolations violations) {
this(violations, false, false, false, true, Collections.emptySet());
}
ParameterChecker acceptUndefined() {
return new ParameterChecker(violations, true, acceptWildcard, ignoreX, caseSensitive, predefined);
}
ParameterChecker acceptWildcard() {
return new ParameterChecker(violations, acceptUndefined, true, ignoreX, caseSensitive, predefined);
}
ParameterChecker ignoreX(boolean ignoreX) {
return new ParameterChecker(violations, acceptUndefined, acceptWildcard, ignoreX, caseSensitive, predefined);
}
ParameterChecker caseSensitive(boolean caseSensitive) {
return new ParameterChecker(violations, acceptUndefined, acceptWildcard, ignoreX, caseSensitive, predefined);
}
ParameterChecker predefined(Set predefined) {
return new ParameterChecker(violations, acceptUndefined, acceptWildcard, ignoreX, caseSensitive, predefined);
}
public Set checkParameters(Map params, Values values, Message message) {
final Map> listParams = new HashMap<>();
addToMapOfList(params, listParams);
return checkListParameters(listParams, values, message);
}
private void addToMapOfList(Map params, Map> listParams) {
for (final Map.Entry entry : params.entrySet()) {
listParams.put(entry.getKey(), Collections.singletonList(entry.getValue()));
}
}
private boolean acceptUndefined(String name) {
return acceptUndefined || predefined.contains(name) || (ignoreX && name.startsWith("x-"));
}
public Set checkListParameters(Map> params, Values values, Message message) {
final Set found = new HashSet<>();
for (final Map.Entry> entry : values) {
final Message namedMsg = message.withParam(entry.getKey());
final String paramName = findMatchingParamName(params.keySet(), entry.getKey());
final List extends AbstractParam> parameters = params.get(paramName);
if (parameters == null || parameters.isEmpty()) {
violations.addIf(!acceptUndefined(entry.getKey().toLowerCase(Locale.ENGLISH)), namedMsg.withMessageParam("undefined"));
} else {
for (final AbstractParam parameter : parameters) {
violations.addIf(!parameter.isRepeat() && entry.getValue().size() > 1, namedMsg.withMessageParam("repeat.superfluous"));
for (final Object value : entry.getValue()) {
checkParameter(parameter, value, namedMsg);
}
}
found.add(paramName);
}
}
for (final Map.Entry> entry : params.entrySet()) {
final Message namedMsg = message.withParam(entry.getKey());
for (final 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) {
final String normalName = normalizeName(name);
for (final String param : paramNames) {
final String normalParam = normalizeName(param);
final int pos = normalParam.indexOf(WILDCARD);
if (normalParam.equals(normalName) || (acceptWildcard && pos >= 0 &&
nameMatchesKeyStart(normalName, normalParam, pos) && nameMatchesKeyEnd(normalName, normalParam, pos))) {
return param;
}
}
return null;
}
private String normalizeName(String name) {
return caseSensitive ? name : name.toLowerCase(Locale.ENGLISH);
}
private boolean nameMatchesKeyStart(String name, String key, int wildcardPos) {
return wildcardPos == 0 || name.startsWith(key.substring(0, wildcardPos));
}
private boolean nameMatchesKeyEnd(String name, String key, int wildcardPos) {
return wildcardPos == key.length() - WILDCARD.length() ||
name.endsWith(key.substring(wildcardPos + WILDCARD.length()));
}
public void checkParameter(AbstractParam param, Object value, Message message) {
if (value == null) {
final Message detail = message.withInnerParam(new Message("value", "empty"));
checkNullParameter(param, detail);
} else {
final Message detail = message.withInnerParam(new Message("value", value));
if (value instanceof String) {
checkStringParameter(param, (String) value, detail);
} else if (value instanceof FileValue) {
checkFileParameter(param, 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) {
checkStringParameter(param, "", detail);
} else {
violations.add(detail.withMessageParam("value.empty"));
}
}
private void checkFileParameter(AbstractParam param, 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:
checkBoolean(value, detail);
break;
case DATE:
checkDate(value, detail);
break;
case FILE:
checkFile(detail);
break;
case INTEGER:
checkInteger(param, value, detail);
break;
case NUMBER:
checkNumber(param, value, detail);
break;
case STRING:
checkString(param, value, detail);
break;
default:
throw new RamlCheckerException("Unhandled parameter type '" + param.getType() + "'");
}
}
private void checkString(AbstractParam param, String value, Message detail) {
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()));
}
private void checkNumber(AbstractParam param, String value, Message detail) {
if (NUMBER.matcher(value).matches()) {
if ("inf".equals(value) || "-inf".equals(value) || "nan".equals(value)) {
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"));
}
}
private void checkInteger(AbstractParam param, String value, Message detail) {
if (INTEGER.matcher(value).matches()) {
checkNumericLimits(param, new BigDecimal(value), detail);
} else {
violations.add(detail.withMessageParam("integer.invalid"));
}
}
private void checkFile(Message detail) {
violations.add(detail.withMessageParam("file.invalid"));
}
private void checkDate(String value, Message detail) {
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"));
}
}
private void checkBoolean(String value, Message detail) {
violations.addIf(!"true".equals(value) && !"false".equals(value), detail.withMessageParam("boolean.invalid"));
}
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