io.fabric8.maven.core.config.ProcessorConfig.java Source code

Java tutorial

Introduction

Here is the source code for io.fabric8.maven.core.config.ProcessorConfig.java

Source

/*
 * Copyright 2016 Red Hat, Inc.
 *
 * Red Hat licenses this file to you 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 io.fabric8.maven.core.config;

import java.util.*;

import com.fasterxml.jackson.annotation.JsonProperty;
import org.apache.commons.lang3.StringUtils;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * Configuration for enrichers and generators
 *
 * @author roland
 * @since 24/07/16
 */
public class ProcessorConfig {

    public static final ProcessorConfig EMPTY = new ProcessorConfig();
    /**
     * Modules to includes, should holde <code>&lt;include&gt;</code> elements
     */
    @Parameter
    @JsonProperty(value = "includes")
    List<String> includes = new ArrayList<>();

    /**
     * Modules to excludes, should hold <code>&lt;exclude&gt;</code> elements
     */
    @Parameter
    @JsonProperty(value = "excludes")
    Set<String> excludes = new HashSet<>();

    /**
     * Configuration for enricher / generators
     */
    // See http://stackoverflow.com/questions/38628399/using-map-of-maps-as-maven-plugin-parameters/38642613 why
    // a "TreeMap" is used as parameter and not "Map<String, String>"
    @Parameter
    @JsonProperty(value = "config")
    Map<String, TreeMap> config = new HashMap<>();

    public ProcessorConfig() {
    }

    public ProcessorConfig(List<String> includes, Set<String> excludes, Map<String, TreeMap> config) {
        this.includes = includes != null ? includes : Collections.<String>emptyList();
        this.excludes = excludes != null ? excludes : Collections.<String>emptySet();
        if (config != null) {
            this.config = config;
        }
    }

    public String getConfig(String name, String key) {
        TreeMap processorMap = config.get(name);
        return processorMap != null ? (String) processorMap.get(key) : null;
    }

    /**
     * Return full configuration as raw string-string values
     *
     * @param name name of the enricher / generator
     * @return unmodifiable map of the original config
     */
    public Map<String, String> getConfigMap(String name) {
        return config.containsKey(name) ? Collections.unmodifiableMap(config.get(name))
                : Collections.<String, String>emptyMap();
    }

    /**
     * Order elements according to the order provided by the include statements.
     * If no includes has been configured, return the given list unaltered.
     * Otherwise arrange the elements from the list in to the include order and return a new
     * list.
     *
     * If an include specifies an element which does not exist, an exception is thrown.
     *
     * @param namedList the list to order
     * @param type a description used in an error message (like 'generator' or 'enricher')
     * @param <T> the concrete type
     * @return the ordered list according to the algorithm described above
     * @throws IllegalArgumentException if the includes reference an non existing element
     */
    public <T extends Named> List<T> prepareProcessors(List<T> namedList, String type) {
        List<T> ret = new ArrayList<>();
        Map<String, T> lookup = new HashMap<>();
        for (T named : namedList) {
            lookup.put(named.getName(), named);
        }
        for (String inc : includes) {
            if (use(inc)) {
                T named = lookup.get(inc);
                if (named == null) {
                    List<String> keys = new ArrayList<>(lookup.keySet());
                    Collections.sort(keys);
                    throw new IllegalArgumentException("No " + type + " with name '" + inc + "' found to include. "
                            + "Please check spelling in your profile / config and your project dependencies. Included "
                            + type + "s: " + StringUtils.join(keys, ", "));
                }
                ret.add(named);
            }
        }
        return ret;
    }

    public boolean use(String inc) {
        return !excludes.contains(inc) && includes.contains(inc);
    }

    /**
     * Clone a processorConfig, resulting in a copy of the given config
     *
     * @param processorConfig config to clone
     * @return the cloned config
     */
    public static ProcessorConfig cloneProcessorConfig(ProcessorConfig processorConfig) {
        return mergeProcessorConfigs(processorConfig);
    }

    /**
     * Merge in another processor configuration, with a lower priority. I.e. the latter a config is
     * in the argument list, the less priority it has. This means:
     *
     * <ul>
     *     <li>A configuration earlier in the list overrides configuration later</li>
     *     <li>Includes and exclude earlier in the list take precedence of the includes/excludes later in the list</li>
     * </ul>
     *
     * @param processorConfigs configs to merge into the current config
     * @return a merged configuration for convenience of chaining and returning. This is a new object and can e.g.
     */
    public static ProcessorConfig mergeProcessorConfigs(ProcessorConfig... processorConfigs) {
        // Merge the configuration
        Map<String, TreeMap> configs = mergeConfig(processorConfigs);

        // Get all includes
        Set<String> excludes = mergeExcludes(processorConfigs);

        // Find the set of includes, which are the ones from the profile + the ones configured
        List<String> includes = mergeIncludes(processorConfigs);

        return new ProcessorConfig(includes, excludes, configs);
    }

    private static Set<String> mergeExcludes(ProcessorConfig... configs) {
        Set<String> ret = new HashSet<>();
        for (ProcessorConfig config : configs) {
            if (config != null) {
                Set<String> excludes = config.excludes;
                if (excludes != null) {
                    ret.addAll(excludes);
                }
            }
        }
        return ret;
    }

    private static List<String> mergeIncludes(ProcessorConfig... configs) {
        List<String> ret = new ArrayList<>();
        for (ProcessorConfig config : configs) {
            if (config != null) {
                List<String> includes = config.includes;
                if (includes != null) {
                    ret.addAll(includes);
                }
            }
        }
        return removeDups(ret);
    }

    // Remove duplicates such that the earlier element remains and the latter is removed
    // Only good for small list (that's what we expect for enrichers and generators)
    private static List<String> removeDups(List<String> list) {
        List<String> ret = new ArrayList<>();
        for (String el : list) {
            if (!ret.contains(el)) {
                ret.add(el);
            }
        }
        return ret;
    }

    private static Map<String, TreeMap> mergeConfig(ProcessorConfig... processorConfigs) {
        Map<String, TreeMap> ret = new HashMap<>();
        if (processorConfigs.length > 0) {
            // Reverse iteration order so that earlier entries have a higher precedence
            for (int i = processorConfigs.length - 1; i >= 0; i--) {
                ProcessorConfig processorConfig = processorConfigs[i];
                if (processorConfig != null) {
                    Map<String, TreeMap> config = processorConfig.config;
                    if (config != null) {
                        for (Map.Entry<String, TreeMap> entry : config.entrySet()) {
                            TreeMap newValues = entry.getValue();
                            if (newValues != null) {
                                TreeMap existing = ret.get(entry.getKey());
                                if (existing == null) {
                                    ret.put(entry.getKey(), new TreeMap(newValues));
                                } else {
                                    for (Map.Entry newValue : (Set<Map.Entry>) newValues.entrySet()) {
                                        existing.put(newValue.getKey(), newValue.getValue());
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        return ret.size() > 0 ? ret : null;
    }

}