com.facebook.buck.core.config.AbstractAliasConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.core.config.AbstractAliasConfig.java

Source

/*
 * Copyright 2018-present Facebook, Inc.
 *
 * Licensed 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 com.facebook.buck.core.config;

import com.facebook.buck.core.exceptions.HumanReadableException;
import com.facebook.buck.core.model.BuildTarget;
import com.facebook.buck.core.model.EmptyTargetConfiguration;
import com.facebook.buck.core.util.immutables.BuckStyleTuple;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ImmutableSetMultimap;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.SetMultimap;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.immutables.value.Value;

/** Contains the logic for handling the [alias] section of .buckconfig. */
@Value.Immutable
@BuckStyleTuple
abstract class AbstractAliasConfig implements ConfigView<BuckConfig> {
    private static final String SECTION = "alias";

    public static AliasConfig from(BuckConfig config) {
        return config.getView(AliasConfig.class);
    }

    /**
     * This pattern is designed so that a fully-qualified build target cannot be a valid alias name
     * and vice-versa.
     */
    private static final Pattern ALIAS_PATTERN = Pattern.compile("[a-zA-Z_-][a-zA-Z0-9_-]*");

    public ImmutableMap<String, String> getEntries() {
        return getDelegate().getEntriesForSection(SECTION);
    }

    @Value.Lazy
    public ImmutableSetMultimap<String, BuildTarget> getAliases() {
        return createAliasToBuildTargetMap(getEntries());
    }

    public ImmutableSet<String> getBuildTargetForAliasAsString(String possiblyFlavoredAlias) {
        String[] parts = possiblyFlavoredAlias.split("#", 2);
        String unflavoredAlias = parts[0];
        ImmutableSet<BuildTarget> buildTargets = getBuildTargetsForAlias(unflavoredAlias);
        if (buildTargets.isEmpty()) {
            return ImmutableSet.of();
        }
        String suffix = parts.length == 2 ? "#" + parts[1] : "";
        return buildTargets.stream().map(buildTarget -> buildTarget.getFullyQualifiedName() + suffix)
                .collect(ImmutableSet.toImmutableSet());
    }

    public ImmutableSet<BuildTarget> getBuildTargetsForAlias(String unflavoredAlias) {
        return getAliases().get(unflavoredAlias);
    }

    public static void validateAliasName(String aliasName) throws HumanReadableException {
        validateAgainstAlias(aliasName, "Alias");
    }

    public static void validateLabelName(String aliasName) throws HumanReadableException {
        validateAgainstAlias(aliasName, "Label");
    }

    /**
     * Create a map of {@link BuildTarget} base paths to aliases. Note that there may be more than one
     * alias to a base path, so the first one listed in the .buckconfig will be chosen.
     */
    public ImmutableMap<Path, String> getBasePathToAliasMap() {
        ImmutableMap<String, String> aliases = getEntries();
        if (aliases.isEmpty()) {
            return ImmutableMap.of();
        }

        // Build up the Map with an ordinary HashMap because we need to be able to check whether the Map
        // already contains the key before inserting.
        Map<Path, String> basePathToAlias = new HashMap<>();
        for (Map.Entry<String, BuildTarget> entry : getAliases().entries()) {
            String alias = entry.getKey();
            BuildTarget buildTarget = entry.getValue();

            Path basePath = buildTarget.getBasePath();
            if (!basePathToAlias.containsKey(basePath)) {
                basePathToAlias.put(basePath, alias);
            }
        }
        return ImmutableMap.copyOf(basePathToAlias);
    }

    /**
     * In a {@link BuckConfig}, an alias can either refer to a fully-qualified build target, or an
     * alias defined earlier in the {@code alias} section. The mapping produced by this method
     * reflects the result of resolving all aliases as values in the {@code alias} section.
     */
    private ImmutableSetMultimap<String, BuildTarget> createAliasToBuildTargetMap(
            ImmutableMap<String, String> rawAliasMap) {
        // We use a LinkedHashMap rather than an ImmutableMap.Builder because we want both (1) order to
        // be preserved, and (2) the ability to inspect the Map while building it up.
        SetMultimap<String, BuildTarget> aliasToBuildTarget = LinkedHashMultimap.create();
        for (Map.Entry<String, String> aliasEntry : rawAliasMap.entrySet()) {
            String alias = aliasEntry.getKey();
            validateAliasName(alias);

            // Determine whether the mapping is to a build target or to an alias.
            List<String> values = Splitter.on(' ').splitToList(aliasEntry.getValue());
            for (String value : values) {
                Set<BuildTarget> buildTargets;
                if (isValidAliasName(value)) {
                    buildTargets = aliasToBuildTarget.get(value);
                    if (buildTargets.isEmpty()) {
                        throw new HumanReadableException("No alias for: %s.", value);
                    }
                } else if (value.isEmpty()) {
                    continue;
                } else {
                    // Here we parse the alias values with a BuildTargetParser to be strict. We could be
                    // looser and just grab everything between "//" and ":" and assume it's a valid base path.
                    buildTargets = ImmutableSet.of(getDelegate().getBuildTargetForFullyQualifiedTarget(value,
                            EmptyTargetConfiguration.INSTANCE));
                }
                aliasToBuildTarget.putAll(alias, buildTargets);
            }
        }
        return ImmutableSetMultimap.copyOf(aliasToBuildTarget);
    }

    /**
     * @return whether {@code aliasName} conforms to the pattern for a valid alias name. This does not
     *     indicate whether it is an alias that maps to a build target in a BuckConfig.
     */
    private static boolean isValidAliasName(String aliasName) {
        return ALIAS_PATTERN.matcher(aliasName).matches();
    }

    private static void validateAgainstAlias(String aliasName, String fieldName) {
        if (isValidAliasName(aliasName)) {
            return;
        }

        if (aliasName.isEmpty()) {
            throw new HumanReadableException("%s cannot be the empty string.", fieldName);
        }

        throw new HumanReadableException("Not a valid %s: %s.", fieldName.toLowerCase(), aliasName);
    }
}