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

com.networknt.oas.validator.ValidatorBase Maven / Gradle / Ivy

There is a newer version: 2.1.38
Show newest version
/*******************************************************************************
 *  Copyright (c) 2017 ModelSolv, Inc. and others.
 *  All rights reserved. This program and the accompanying materials
 *  are made available under the terms of the Eclipse Public License v1.0
 *  which accompanies this distribution, and is available at
 *  http://www.eclipse.org/legal/epl-v10.html
 *
 *  Contributors:
 *     ModelSolv, Inc. - initial API and implementation and/or initial documentation
 *******************************************************************************/
package com.networknt.oas.validator;

import com.networknt.oas.jsonoverlay.JsonOverlay;
import com.networknt.oas.validator.oasparser.fake.scheme.Handler;

import javax.print.attribute.standard.Severity;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;

import static com.networknt.oas.validator.Messages.m;
import static com.networknt.oas.validator.NumericUtils.*;

public abstract class ValidatorBase implements Validator {

    private static final String EMAIL_PATTERN =
            "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@"
                    + "[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$";
    private static Pattern pattern = Pattern.compile(EMAIL_PATTERN);

    @Override
    public ValidationResults validate(T object) {
        ValidationResults results = new ValidationResults();
        validate(object, results);
        return results;
    }

    @Override
    public void validate(final T object, final ValidationResults results, String crumb) {
        results.withCrumb(crumb, new Runnable() {
            @Override
            public void run() {
                validate(object, results);
            }
        });
    }

    public void validateString(final String value, final ValidationResults results, final boolean required,
            final String crumb) {
        validateString(value, results, required, (Pattern) null, crumb);
    }

    public void validateString(final String value, final ValidationResults results, final boolean required,
            final String pattern, final String crumb) {
        validateString(value, results, required, Pattern.compile(pattern), crumb);
    }

    public void validateString(final String value, final ValidationResults results, final boolean required,
            final Pattern pattern, final String crumb) {
        checkMissing(required, value, results, crumb);
        if (value != null && pattern != null && !pattern.matcher(value).matches()) {
            results.addError(m.msg("PatternMatchFail|String value does not match required pattern", value, pattern),
                    crumb);
        }
    }

    public void validateUrl(String value, ValidationResults results, boolean required, String crumb) {
        validateUrl(value, results, required, crumb, false);
    }

    public void validateUrl(String value, ValidationResults results, boolean required, String crumb,
            boolean allowVars) {
        validateUrl(value, results, required, crumb, allowVars, Severity.ERROR);
    }

    public void validateUrl(final String value, final ValidationResults results, boolean required, String crumb,
            final boolean allowVars, final Severity severity) {
        validateString(value, results, required, (Pattern) null, crumb);
        if (value != null) {
            checkUrl(value, results, allowVars, severity, crumb);
        }
    }

    public void validateEmail(final String value, final ValidationResults results, boolean required, String crumb) {
        validateString(value, results, required, (Pattern) null, crumb);
        if (value != null) {
            checkEmail(value, results, crumb);
        }
    }

    public  void validatePositive(final N value, final ValidationResults results,
            final boolean required, final String crumb) {
        checkMissing(required, value, results, crumb);
        if (value != null && le(value, zero(value))) {
            results.addError(m.msg("ReqPositive|Value must be strictly positive", crumb, value), crumb);
        }
    }

    public  void validateNonNegative(final N value, final ValidationResults results,
            final boolean required, final String crumb) {
        checkMissing(required, value, results, crumb);
        if (value != null && lt(value, zero(value))) {
            results.addError(m.msg("ReqPositive|Value must be strictly positive", crumb, value), crumb);
        }
    }

    public void validateNonEmpty(Collection values, boolean hasValues, ValidationResults results, String crumb) {
        if (hasValues && values.isEmpty()) {
            results.addError(m.msg("EmptyColl|Collection may not be empty"), crumb);
        }
    }

    public  void validateUnique(final Collection values, final ValidationResults results, final String crumb) {
        results.withCrumb(crumb, new Runnable() {
            Set seen = new HashSet<>();

            @Override
            public void run() {
                int i = 0;
                for (V value : values) {
                    if (seen.contains(value)) {
                        results.addError(m.msg("DuplicateValue|Value appeared already", value, crumb), "[" + i + "]");
                    } else {
                        seen.add(value);
                    }
                    i += 1;
                }
            }
        });
    }

    public void validatePattern(final String pattern, final ValidationResults results, final boolean required,
            final String crumb) {
        checkMissing(required, pattern, results, crumb);
        if (pattern != null) {
            try {
                Pattern.compile(pattern);
            } catch (PatternSyntaxException e) {
                results.addWarning(
                        m.msg("BadPattern|Pattern is not a valid Java Regular Expression but may be valid ECMA 262",
                                pattern),
                        crumb);
            }
        }
    }

    public  void validateField(final F value, final ValidationResults results, final boolean required,
            final String crumb, final Validator validator) {
        checkMissing(required, value, results, crumb);
        if (isPresent(value)) {
            validator.validate(value, results, crumb);
        }
    }

    public  void validateList(final Collection value, final boolean isPresent,
            final ValidationResults results, final boolean required, final String crumb, final Validator validator) {
        if (required && !isPresent) {
            results.addError(m.msg("MissingField|Required field is missing", crumb), crumb);
        }
        if (isPresent && validator != null) {
            int i = 0;
            for (V element : value) {
                validator.validate(element, results, "[" + i++ + "]");
            }
        }
    }

    public  void validateMap(final Map value, final ValidationResults results,
            final boolean required, final String crumb, final Pattern pattern, final Validator validator) {
        checkMissing(required, value, results, crumb);
        results.withCrumb(crumb, new Runnable() {

            @Override
            public void run() {
                for (Entry entry : value.entrySet()) {
                    checkKey(entry.getKey(), pattern, results);
                    if (validator != null) {
                        validator.validate(entry.getValue(), results, entry.getKey());
                    }
                }
            }
        });
    }

    public void validateExtensions(final Map extensions, final ValidationResults results) {
        validateExtensions(extensions, results, null);
    }

    public void validateExtensions(final Map extensions, final ValidationResults results,
            String crumb) {
        validateMap(extensions, results, false, crumb, null, null);
    }

    public void validateFormat(String format, String type, ValidationResults results, String crumb) {
        if (format != null && type != null) {
            String normalType = null;
            switch (format) {
            case "int32":
            case "int64":
                normalType = "integer";
                break;
            case "float":
            case "double":
                normalType = "number";
                break;
            case "byte":
            case "binary":
            case "date":
            case "date-time":
            case "password":
                normalType = "string";
                break;
            }
            if (normalType != null && !type.equals(normalType)) {
                results.addWarning(m.msg("WrongTypeFormat|OpenAPI-defined format used with nonstandard type", format,
                        type, normalType), crumb);
            }
        }
    }

    public void validateDefault(Object defaultValue, String type, ValidationResults results, String crumb) {
        if (defaultValue != null && type != null) {
            boolean ok = false;
            switch (type) {
            case "string":
                ok = defaultValue instanceof String;
                break;
            case "number":
                ok = NumericUtils.isNumeric(defaultValue);
                break;
            case "integer":
                ok = NumericUtils.isIntegral(defaultValue);
                break;
            case "boolean":
                ok = defaultValue instanceof Boolean;
                break;
            case "object":
                ok = defaultValue instanceof Map;
                break;
            case "array":
                ok = defaultValue instanceof Collection;
                break;
            }
            if (!ok) {
                results.addError(m.msg("WrongTypeValue|Value is incompatible with schema type", type, defaultValue),
                        crumb);
            }
        }
    }

    private void checkMissing(boolean required, final Object value, final ValidationResults results,
            final String crumb) {
        if (required && !isPresent(value)) {
            results.addError(m.msg("MissingField|Required field is missing", crumb), crumb);
        }
    }

    private void checkKey(final String key, final Pattern pattern, final ValidationResults results) {
        if (pattern != null && !pattern.matcher(key).matches()) {
            results.addError(m.msg("BadKey|Invalid key in map", key, pattern), key);
        }
    }

    public static final String SPECIAL_SCHEME = Handler.class.getPackage().getName()
            .substring(ValidatorBase.class.getPackage().getName().length() + 1);
    private static boolean specialSchemeInited = false;

    private void checkUrl(String url, ValidationResults results, boolean allowVars, Severity severity, String crumb) {
        // TODO Q: Any help from spec in being able to validate URLs with vars? E.g is our treatment here valid? We
        // assume vars can only appear where simple text can appear, so handling vars means relacing {.*} with "1" and
        // testing for URL validity. We use a digit instead of a letter because it covers vars in the port, and
        // elsewhere digits are always allowed where letters are.
        String origUrl = url;
        if (allowVars) {
            url = url.replaceAll("\\{[^}]+\\}", "1");
            if (url.startsWith("1:")) {
                // "1" is not a valid scheme name, so we need to replace it with special scheme, for which we provide a
                // do-nothing protocol handler implementation
                url = SPECIAL_SCHEME + url.substring(1);
                if (!specialSchemeInited) {
                    // register protocol handler for special scheme
                    initSpecialScheme();
                }
            }
        }
        try {
            new URL(url);
        } catch (MalformedURLException e) {
            results.addError(m.msg("BadUrl|Invalid URL", origUrl, e.toString()), crumb);
        }
    }

    private void initSpecialScheme() {
        String prop = "java.protocol.handler.pkgs";
        String former = System.getProperty(prop);
        try {
            System.setProperty(prop, ValidatorBase.class.getPackage().getName());
            new URL(SPECIAL_SCHEME + ":");
        } catch (MalformedURLException e) {
        } finally {
            if (former != null) {
                System.setProperty(prop, former);
            } else {
                System.getProperties().remove(prop);
            }
        }
        specialSchemeInited = true;
    }

    private void checkEmail(String email, ValidationResults results, String crumb) {
        Matcher matcher = pattern.matcher(email);
        if(!matcher.matches()) {
            results.addError(m.msg("BadEmail|Invalid email address", email), crumb);
        }
    }

    private boolean isPresent(Object value) {
		return value instanceof JsonOverlay ? ((JsonOverlay) value).isPresent() : value != null;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy