private EndpointValidationResult validateProperties()

in core/camel-core-catalog/src/main/java/org/apache/camel/catalog/impl/AbstractCamelCatalog.java [199:430]


    private EndpointValidationResult validateProperties(
            String scheme, Map<String, String> properties,
            boolean lenient, boolean consumerOnly,
            boolean producerOnly) {
        EndpointValidationResult result = new EndpointValidationResult(scheme);

        ComponentModel model = componentModel(scheme);
        Map<String, BaseOptionModel> rows = new HashMap<>();
        model.getComponentOptions().forEach(o -> rows.put(o.getName(), o));
        // endpoint options have higher priority so overwrite component options
        model.getEndpointOptions().forEach(o -> rows.put(o.getName(), o));
        model.getEndpointPathOptions().forEach(o -> rows.put(o.getName(), o));

        if (model.isApi()) {
            String[] apiSyntax = StringHelper.splitWords(model.getApiSyntax());
            String key = properties.get(apiSyntax[0]);
            String key2 = apiSyntax.length > 1 ? properties.get(apiSyntax[1]) : null;
            Map<String, BaseOptionModel> apiProperties = extractApiProperties(model, key, key2);
            rows.putAll(apiProperties);
        }

        // the dataformat component refers to a data format so lets add the properties for the selected
        // data format to the list of rows
        if ("dataformat".equals(scheme)) {
            String dfName = properties.get("name");
            if (dfName != null) {
                DataFormatModel dfModel = dataFormatModel(dfName);
                if (dfModel != null) {
                    dfModel.getOptions().forEach(o -> rows.put(o.getName(), o));
                }
            }
        }

        for (Map.Entry<String, String> property : properties.entrySet()) {
            String value = property.getValue();
            String originalName = property.getKey();
            // the name may be using an optional prefix, so lets strip that because the options
            // in the schema are listed without the prefix
            String name = stripOptionalPrefixFromName(rows, originalName);
            // the name may be using a prefix, so lets see if we can find the real property name
            String propertyName = getPropertyNameFromNameWithPrefix(rows, name);
            if (propertyName != null) {
                name = propertyName;
            }
            BaseOptionModel row = rows.get(name);
            if (row == null) {
                // unknown option

                // only add as error if the component is not lenient properties, or not stub component
                // and the name is not a property placeholder for one or more values
                boolean namePlaceholder = name.startsWith("{{") && name.endsWith("}}");
                if (!namePlaceholder && !"stub".equals(scheme)) {
                    if (lenient) {
                        // as if we are lenient then the option is a dynamic extra option which we cannot validate
                        result.addLenient(name);
                    } else {
                        // its unknown
                        result.addUnknown(name);
                        if (suggestionStrategy != null) {
                            String[] suggestions = suggestionStrategy.suggestEndpointOptions(rows.keySet(), name);
                            if (suggestions != null) {
                                result.addUnknownSuggestions(name, suggestions);
                            }
                        }
                    }
                }
            } else {
                if ("parameter".equals(row.getKind())) {
                    // consumer only or producer only mode for parameters
                    String label = row.getLabel();
                    if (consumerOnly) {
                        if (label != null && label.contains("producer")) {
                            // the option is only for producer so you cannot use it in consumer mode
                            result.addNotConsumerOnly(name);
                        }
                    } else if (producerOnly) {
                        if (label != null && label.contains("consumer")) {
                            // the option is only for consumer so you cannot use it in producer mode
                            result.addNotProducerOnly(name);
                        }
                    }
                }

                String prefix = row.getPrefix();
                boolean valuePlaceholder = value.startsWith("{{") || value.startsWith("${") || value.startsWith("$simple{");
                boolean lookup = value.startsWith("#") && value.length() > 1;
                // we cannot evaluate multi values as strict as the others, as we don't know their expected types
                boolean multiValue = prefix != null && originalName.startsWith(prefix)
                        && row.isMultiValue();

                // default value
                Object defaultValue = row.getDefaultValue();
                if (defaultValue != null) {
                    result.addDefaultValue(name, defaultValue.toString());
                }

                // is required but the value is empty
                if (row.isRequired() && CatalogHelper.isEmpty(value)) {
                    result.addRequired(name);
                }

                // is the option deprecated
                boolean deprecated = row.isDeprecated();
                if (deprecated) {
                    result.addDeprecated(name);
                }

                // is enum but the value is not within the enum range
                // but we can only check if the value is not a placeholder
                List<String> enums = row.getEnums();
                if (!multiValue && !valuePlaceholder && !lookup && enums != null) {
                    boolean found = false;
                    for (String s : enums) {
                        String dashEC = StringHelper.camelCaseToDash(value);
                        String valueEC = StringHelper.asEnumConstantValue(value);
                        if (value.equalsIgnoreCase(s) || dashEC.equalsIgnoreCase(s) || valueEC.equalsIgnoreCase(s)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        handleNotFound(result, value, name, enums);
                    }
                }

                // is reference lookup of bean (not applicable for @UriPath, enums, or multi-valued)
                if (!multiValue && enums == null && !"path".equals(row.getKind()) && "object".equals(row.getType())) {
                    // must start with # and be at least 2 characters
                    if (!value.startsWith("#") || value.length() <= 1) {
                        result.addInvalidReference(name, value);
                    }
                }

                // is boolean
                if (!multiValue && !valuePlaceholder && !lookup && "boolean".equals(row.getType())) {
                    // value must be a boolean
                    boolean bool = ObjectHelper.isBoolean(value);
                    if (!bool) {
                        result.addInvalidBoolean(name, value);
                    }
                }

                // is duration
                if (!multiValue && !valuePlaceholder && !lookup && "duration".equals(row.getType())) {
                    // value must be convertable to a duration
                    boolean valid = validateDuration(value);
                    if (!valid) {
                        result.addInvalidDuration(name, value);
                    }
                }

                // is integer
                if (!multiValue && !valuePlaceholder && !lookup && "integer".equals(row.getType())) {
                    // value must be an integer
                    boolean valid = validateInteger(value);
                    if (!valid) {
                        result.addInvalidInteger(name, value);
                    }
                }

                // is number
                if (!multiValue && !valuePlaceholder && !lookup && "number".equals(row.getType())) {
                    // value must be an number
                    boolean valid = false;
                    try {
                        valid = !Double.valueOf(value).isNaN() || !Float.valueOf(value).isNaN();
                    } catch (Exception e) {
                        // ignore
                    }
                    if (!valid) {
                        result.addInvalidNumber(name, value);
                    }
                }
            }
        }

        // for api component then check that the apiName/methodName combo is valid
        if (model.isApi()) {
            String[] apiSyntax = StringHelper.splitWords(model.getApiSyntax());
            String key1 = properties.get(apiSyntax[0]);
            String key2 = apiSyntax.length > 1 ? properties.get(apiSyntax[1]) : null;

            if (key1 != null && key2 != null) {
                ApiModel api = model.getApiOptions().stream().filter(o -> o.getName().equalsIgnoreCase(key1)).findFirst()
                        .orElse(null);
                if (api == null) {
                    result.addInvalidEnum(apiSyntax[0], key1);
                    result.addInvalidEnumChoices(apiSyntax[0],
                            model.getApiOptions().stream().map(ApiModel::getName).toArray(String[]::new));
                } else {
                    // walk each method and match against its name/alias
                    boolean found = false;
                    for (ApiMethodModel m : api.getMethods()) {
                        String key3 = apiMethodAlias(api, m);
                        if (m.getName().equalsIgnoreCase(key2) || key2.equalsIgnoreCase(key3)) {
                            found = true;
                            break;
                        }
                    }
                    if (!found) {
                        result.addInvalidEnum(apiSyntax[1], key2);

                        result.addInvalidEnumChoices(apiSyntax[1], api.getMethods().stream()
                                .map(m -> {
                                    // favour using method alias in choices
                                    String answer = apiMethodAlias(api, m);
                                    if (answer == null) {
                                        answer = m.getName();
                                    }
                                    return answer;
                                }).toArray(String[]::new));
                    }
                }
            }
        }

        // now check if all required values are there, and that a default value does not exist
        for (BaseOptionModel row : rows.values()) {
            if (row.isRequired()) {
                String name = row.getName();
                Object value = properties.get(name);
                if (CatalogHelper.isEmpty(value)) {
                    value = row.getDefaultValue();
                }
                if (CatalogHelper.isEmpty(value)) {
                    result.addRequired(name);
                }
            }
        }

        return result;
    }