001/**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.camel.component.extension.verifier;
018
019import java.util.ArrayList;
020import java.util.Arrays;
021import java.util.Collection;
022import java.util.Collections;
023import java.util.HashSet;
024import java.util.List;
025import java.util.Map;
026import java.util.Optional;
027import java.util.Set;
028import java.util.TreeSet;
029import java.util.stream.Collectors;
030
031import org.apache.camel.component.extension.ComponentVerifierExtension.VerificationError;
032import org.apache.camel.util.ObjectHelper;
033
034/**
035 * Helper that validates component parameters.
036 */
037public final class ResultErrorHelper {
038
039    private ResultErrorHelper() {
040    }
041
042    // **********************************
043    // Helpers
044    // **********************************
045
046    /**
047     *
048     * @param parameterName the required option
049     * @param parameters the
050     * @return
051     */
052    public static Optional<VerificationError> requiresOption(String parameterName, Map<String, Object> parameters) {
053        if (ObjectHelper.isEmpty(parameters.get(parameterName))) {
054            return Optional.of(ResultErrorBuilder.withMissingOption(parameterName).build());
055        }
056
057        return Optional.empty();
058    }
059
060    /**
061     * Validates that the given parameters satisfy any grouped options
062     * ({@link OptionsGroup}). A parameter set is valid if it is
063     * present and required by least one of the groups.
064     *
065     * <p>As an example consider that there are two option groups that
066     * can be specified:
067     * <ul>
068     * <li>optionA: requires param1 and param2
069     * <li>optionB: requires param1 and param3
070     * </ul>
071     *
072     * Valid parameters are those that include param1 and either param2
073     * and/or param3.
074     *
075     * <p>Note the special syntax of {@link OptionsGroup#getOptions()}
076     * that can require an property ({@code "propertyName"}) or can
077     * forbid the presence of a property ({@code "!propertyName"}).
078     *
079     * <p>With that if in the example above if param2 is specified
080     * specifying param3 is not allowed, and vice versa option groups
081     * should be defined with options:
082     * <ul>
083     * <li>optionA: ["param1", "param2", "!param3"]
084     * <li>optionB: ["param1", "!param2", "param3"]
085     * </ul>
086     *
087     * @param parameters given parameters of a component
088     * @param groups groups of options
089     * @see OptionsGroup
090     */
091    public static List<VerificationError> requiresAny(Map<String, Object> parameters, OptionsGroup... groups) {
092        return requiresAny(parameters, Arrays.asList(groups));
093    }
094
095    /**
096     * Validates that the given parameters satisfy any grouped options
097     * ({@link OptionsGroup}). A parameter set is valid if it is
098     * present and required by least one of the groups.
099     *
100     * @param parameters given parameters of a component
101     * @param groups groups of options
102     * @see #requiresAny(Map, OptionsGroup...)
103     * @see OptionsGroup
104     */
105    public static List<VerificationError> requiresAny(Map<String, Object> parameters, Collection<OptionsGroup> groups) {
106        final List<VerificationError> verificationErrors = new ArrayList<>();
107        final Set<String> keys = new HashSet<>(parameters.keySet());
108
109        for (OptionsGroup group : groups) {
110            final Set<String> required = required(group.getOptions());
111            final Set<String> excluded = excluded(group.getOptions());
112
113            final ResultErrorBuilder builder = new ResultErrorBuilder()
114                .code(VerificationError.StandardCode.ILLEGAL_PARAMETER_GROUP_COMBINATION)
115                .detail(VerificationError.GroupAttribute.GROUP_NAME, group.getName())
116                .detail(VerificationError.GroupAttribute.GROUP_OPTIONS, String.join(",", parameters(group.getOptions())));
117
118            if (keys.containsAll(required)) {
119                // All the options of this group are found so we are good
120                final Set<String> shouldBeExcluded = new HashSet<>(keys);
121                shouldBeExcluded.retainAll(excluded);
122
123                if (shouldBeExcluded.isEmpty()) {
124                    // None of the excluded properties is present, also good
125                    return Collections.emptyList();
126                }
127
128                shouldBeExcluded.forEach(builder::parameterKey);
129                verificationErrors.add(builder.build());
130            } else {
131
132                for (String option : required) {
133                    if (!parameters.containsKey(option)) {
134                        builder.parameterKey(option);
135                    }
136                }
137
138                for (String option : excluded) {
139                    if (parameters.containsKey(option)) {
140                        builder.parameterKey(option);
141                    }
142                }
143
144                verificationErrors.add(builder.build());
145            }
146        }
147
148        return verificationErrors;
149    }
150
151    static Set<String> required(final Set<String> options) {
152        return options.stream().filter(o -> !o.startsWith("!")).collect(Collectors.toSet());
153    }
154
155    static Set<String> excluded(final Set<String> options) {
156        return options.stream().filter(o -> o.startsWith("!")).map(o -> o.substring(1)).collect(Collectors.toSet());
157    }
158
159    static Set<String> parameters(final Set<String> options) {
160        final Set<String> withoutExclusionMark = options.stream().map(o -> o.replaceFirst("!", "")).collect(Collectors.toSet());
161
162        return new TreeSet<String>(withoutExclusionMark);
163    }
164}