RuleParameters.java

package org.thewonderlemming.c4plantuml.linter.rules;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A container for settings to control the behavior of a {@link AbstractLintingRule}.
 *
 * @author thewonderlemming
 *
 */
public class RuleParameters {

    /**
     * A part of the {@link RuleParameters} builder.
     *
     * @author thewonderlemming
     *
     */
    public static interface AddParameter {

        /**
         * Creates a new parameter and adds it to the current parameters list in the builder.
         * <p>
         * It another parameter exists with the same name, then its value will be replaced with the new one.
         *
         * @param name the name of the new parameter.
         * @param value the value of the new parameter.
         * @return the current instance of {@link AddParameter} to allow method chaining.
         */
        AddParameter addParameter(final String name, final String value);

        /**
         * Creates a collection parameters and adds them to the current parameters list in the builder.
         * <p>
         * It another parameter exists with the same name as one of the new parameters, then its value will be replaced
         * with the new value.
         *
         * @param values a {@link Map} of parameters to create and to add to the new instance.
         * @return the current instance of {@link AddParameter} to allow method chaining.
         */
        default AddParameter addParameters(final Map<String, String> values) {

            if (null != values) {
                values.forEach(this::addParameter);
            }

            return this;
        }

        /**
         * Builds the new {@link RuleParameters} instance and returns it.
         *
         * @return the newly built instance.
         */
        RuleParameters build();
    }


    private static final Logger LOGGER = LoggerFactory.getLogger(RuleParameters.class);

    private final Map<String, String> parameters = new HashMap<>();


    /**
     * Returns a fluent builder instance to the {@link RuleParameters} class.
     *
     * @return a new builder instance for {@link RuleParameters}.
     */
    public static AddParameter builder() {

        return new AddParameter() {

            private final Map<String, String> parameters = new HashMap<>();

            @Override
            public AddParameter addParameter(final String name, final String value) {

                this.parameters.put(name, value);
                return this;
            }

            @Override
            public RuleParameters build() {
                return new RuleParameters(parameters);
            }
        };
    }

    private RuleParameters(final Map<String, String> parameters) {
        this.parameters.putAll(parameters);
    }

    /**
     * Retrieves a parameter value by its name.
     *
     * @param parameterName the name of the parameter to retrieve value from.
     * @return an {@link Optional} of the value if found, or empty else.
     */
    public Optional<String> getParameter(final String parameterName) {
        return Optional.ofNullable(this.parameters.get(parameterName));
    }

    /**
     * Retrieves a {@link Boolean} parameter value by its name.
     *
     * @param parameterName parameterName the name of the parameter to retrieve value from.
     * @return an {@link Optional} of the value if found, or empty else.
     */
    public Optional<Boolean> getParameterAsBoolean(final String parameterName) {

        return this.getParameterAsType(parameterName, Boolean.class, value -> {

            if (!value.equalsIgnoreCase("true") && !value.equalsIgnoreCase("false")) {

                LOGGER.error("Error while parsing parameter {} as boolean: got {}", parameterName, value);
                return Optional.empty();
            }

            return Optional.ofNullable(Boolean.valueOf(value));
        });
    }

    /**
     * Retrieves an {@link Integer} parameter value by its name.
     *
     * @param parameterName parameterName the name of the parameter to retrieve value from.
     * @return an {@link Optional} of the value if found, or empty else.
     */
    public Optional<Integer> getParameterAsInteger(final String parameterName) {

        return this.getParameterAsType(parameterName, Integer.class, value -> {

            try {

                final Integer valueAsInt = Integer.parseInt(value);
                return Optional.ofNullable(valueAsInt);

            } catch (final NumberFormatException e) {
                LOGGER.error("Error while parsing parameter {} as integer: {}", parameterName, e.getMessage(), e);
            }

            return Optional.empty();
        });
    }

    /**
     * Retrieves a {@link Long} parameter value by its name.
     *
     * @param parameterName parameterName the name of the parameter to retrieve value from.
     * @return an {@link Optional} of the value if found, or empty else.
     */
    public Optional<Long> getParameterAsLong(final String parameterName) {

        return this.getParameterAsType(parameterName, Long.class, value -> {

            try {

                final Long valueAsLong = Long.parseLong(value);
                return Optional.ofNullable(valueAsLong);

            } catch (final NumberFormatException e) {
                LOGGER.error("Error while parsing parameter {} as long: {}", parameterName, e.getMessage(), e);
            }

            return Optional.empty();
        });
    }

    /**
     * Retrieves a parameter value by its name and casts it to the given type {@code <T>}.
     *
     * @param parameterName parameterName the name of the parameter to retrieve value from.
     * @param typeClass the type to which you want to cast the value to.
     * @param typeConverter the converter function used to cast the parameter value.
     * @param <T> the type to which you want to cast the value.
     * @return an {@link Optional} of the value if found, or empty else.
     */
    public <T> Optional<T> getParameterAsType(final String parameterName, final Class<T> typeClass,
        final Function<String, Optional<T>> typeConverter) {

        final Optional<String> valueAsString = this.getParameter(parameterName);
        return valueAsString.isPresent() ? typeConverter.apply(valueAsString.get()) : Optional.empty();
    }
}