org.cloudbees.literate.api.v1.ExecutionEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudbees.literate.api.v1.ExecutionEnvironment.java

Source

/*
 * The MIT License
 *
 * Copyright (c) 2013-2014, CloudBees, Inc., Amadeus IT Group
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.cloudbees.literate.api.v1;

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import net.jcip.annotations.Immutable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import com.google.common.base.Joiner;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
 * Represents an environment for execution of a series of commands.
 *
 * @author Stephen Connolly
 */
@Immutable
public class ExecutionEnvironment implements Serializable, Comparable<ExecutionEnvironment> {

    /**
     * Ensure consistent serialization.
     */
    private static final long serialVersionUID = 1L;

    /**
     * Save allocation of the environment that has no labels.
     */
    private static final ExecutionEnvironment ANY_EXECUTION_ENVIRONMENT = new ExecutionEnvironment();

    /**
     * The labels of this execution environment.
     */
    @NonNull
    private final Set<String> labels;

    /**
     * Environment variables for this particular execution environment
     */
    @NonNull
    private final Map<String, String> variables;

    /**
     * Default constructor of an empty environment.
     */
    public ExecutionEnvironment() {
        this.labels = Collections.<String>emptySet();
        this.variables = Collections.emptyMap();
    }

    /**
     * Constructor.
     *
     * @param labels collection of labels that define the environment.
     * @param variables map of environment variables applicable to this environment
     */
    public ExecutionEnvironment(@CheckForNull Collection<String> labels,
            @CheckForNull Map<String, String> variables) {
        if (variables != null) {
            this.labels = labels == null ? Collections.<String>emptySet()
                    : Collections.unmodifiableSet(new TreeSet<String>(removeNulls(labels)));
            this.variables = Collections.unmodifiableMap(removeNulls(new TreeMap<String, String>(variables)));
        } else {
            Set<String> l = new TreeSet<String>();
            Map<String, String> v = new TreeMap<String, String>();
            for (String string : removeNulls(labels)) {
                String[] split = string.split("=", 2);
                if (split.length > 1) {
                    v.put(split[0], split[1]);
                } else {
                    l.add(string);
                }
            }
            this.labels = Collections.unmodifiableSet(l);
            this.variables = Collections.unmodifiableMap(v);
        }
    }

    /**
     * Constructor.
     *
     * @param labels collection of labels that define the environment.
     */
    public ExecutionEnvironment(@CheckForNull Collection<String> labels) {
        this(labels, null);
    }

    /**
     * Constructor.
     *
     * @param labels the labels that define the environment.
     */
    public ExecutionEnvironment(String... labels) {
        this(Arrays.asList(labels), null);
    }

    /**
     * Constructor.
     *
     * @param base             a base {@link ExecutionEnvironment} to be extended by the additional labels
     * @param additionalLabels collection of labels that define the environment.
     */
    public ExecutionEnvironment(@CheckForNull ExecutionEnvironment base,
            @CheckForNull Collection<String> additionalLabels) {
        Set<String> labels = new TreeSet<String>();
        Map<String, String> variables = new HashMap<String, String>();
        if (base != null) {
            labels.addAll(base.getLabels());
            variables.putAll(base.getVariables());
        }
        if (additionalLabels != null) {
            labels.addAll(removeNulls(additionalLabels));
        }
        this.labels = Collections.unmodifiableSet(labels);
        this.variables = Collections.unmodifiableMap(variables);
    }

    /**
     * Constructor.
     *
     * @param base             a base {@link ExecutionEnvironment} to be extended by the additional labels
     * @param additionalLabels collection of labels that define the environment.
     */
    public ExecutionEnvironment(@CheckForNull ExecutionEnvironment base, String... additionalLabels) {
        this(base, Arrays.asList(additionalLabels));
    }

    /**
     * Returns the components (labels, variables) that define the environment.
     * 
     * @return the components (labels, variables) that define the environment.
     */
    public Set<String> getComponents() {
        return Sets.union(labels, getVariablesAsSet());
    }

    /**
     * Returns the labels that define the environment.
     *
     * @return the labels that define the environment.
     */
    @NonNull
    public Set<String> getLabels() {
        return labels;
    }

    @NonNull
    public String getDescription() {
        return join("[", Joiner.on(',').join(getComponents()), "]");
    }

    @NonNull
    private String join(String... str) {
        StringBuilder sb = new StringBuilder();
        for (String string : str) {
            sb.append(string);
        }
        return sb.toString();
    }

    /**
     * Returns the environment variables that define the environment
     *
     * @return the environment variables that are applicable to this environment
     */
    public Map<String, String> getVariables() {
        return variables;
    }

    private Set<String> getVariablesAsSet() {
        Set<String> result = Sets.newTreeSet();
        for (Entry<String, String> entry : variables.entrySet()) {
            result.add(entry.getKey() + "=" + entry.getValue());
        }
        return result;
    }

    /**
     * Returns {@code true} if the environment does not specify any labels nor environment variables.
     *
     * @return {@code true} if the environment does not specify any labels nor environment variables.
     */
    public boolean isUnspecified() {
        return labels.isEmpty() && variables.isEmpty();
    }

    /**
     * Tests if this environment satisfies the requirements of the specified environment.
     *
     * @param environment the specified environment.
     * @return {@code true} if and only if all the labels required by the specified environment are provided by this
     *         environment.
     */
    public boolean isMatchFor(ExecutionEnvironment environment) {
        return getLabels().containsAll(environment.getLabels())
                && Maps.difference(getVariables(), environment.getVariables()).entriesOnlyOnRight().isEmpty();
    }

    /**
     * Returns a new {@link ExecutionEnvironment} instance complemented with the given environment variables
     * @param variables the environment variables to add to the execution environment. The values will override existing environment variables if applicable
     * @return a new {@link ExecutionEnvironment} instance complemented with the given environment variables
     */
    public ExecutionEnvironment withVariables(Map<String, String> variables) {
        HashMap<String, String> newMap = new HashMap<String, String>(this.variables);
        newMap.putAll(variables);
        return new ExecutionEnvironment(labels, newMap);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof ExecutionEnvironment)) {
            return false;
        }

        ExecutionEnvironment that = (ExecutionEnvironment) o;

        if (!labels.equals(that.labels)) {
            return false;
        }

        if (!variables.equals(that.variables)) {
            return false;
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE", justification = "It might be null after a deserialization of an old version")
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + labels.hashCode();
        result = prime * result + (variables == null ? 0 : variables.hashCode());
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder("ExecutionEnvironment{");
        sb.append("labels=").append(labels);
        sb.append(",variables=").append(variables);
        sb.append('}');
        return sb.toString();
    }

    /**
     * Removes any {@code null} values from a collection (returns a copy, original remains unmodified).
     *
     * @param collection the collection.
     * @param <T>        the type of elements in the collection.
     * @return either the original collection if {@code null} was not found or a copy builder all {@code null} values
     *         removed.
     */
    private static <T> Collection<T> removeNulls(Collection<T> collection) {
        try {
            if (!collection.contains(null)) {
                return collection;
            }
        } catch (NullPointerException e) {
            // collection does not permit null, so safe to return unmodified.
            return collection;
        }
        List<T> result = new ArrayList<T>();
        for (T t : collection) {
            if (t != null) {
                result.add(t);
            }
        }
        return result;
    }

    /**
     * Removes any {@code null} values from a map (returns a copy, original remains unmodified).
     *
     * @param map the map.
     * @param <K>        the type of key in the map.
     * @param <V>        the type of value in the map.
     * @return either the original map if {@code null} was not found or a copy builder all {@code null} values
     *         removed.
     */
    private static <K, V> Map<K, V> removeNulls(Map<K, V> map) {
        try {
            if (!map.containsValue(null)) {
                return map;
            }
        } catch (NullPointerException e) {
            // map does not permit null, so safe to return unmodified.
            return map;
        }
        Map<K, V> result = new HashMap<K, V>();
        for (Entry<K, V> entry : result.entrySet()) {
            if (entry.getValue() != null) {
                result.put(entry.getKey(), entry.getValue());
            }
        }
        return result;
    }

    /**
     * Returns an environment without labels, that is it will match all other environments.
     *
     * @return an environment without labels.
     */
    public static ExecutionEnvironment any() {
        return ANY_EXECUTION_ENVIRONMENT;
    }

    @Override
    public int compareTo(ExecutionEnvironment o) {
        if (o == null)
            return 1;
        return toString().compareTo(o.toString());
    }

}