com.google.devtools.build.lib.rules.android.AndroidDataConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.rules.android.AndroidDataConverter.java

Source

// Copyright 2018 The Bazel Authors. All rights reserved.
//
// 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.google.devtools.build.lib.rules.android;

import com.google.common.collect.ImmutableList;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.CommandLineItem.ParametrizedMapFn;
import com.google.devtools.build.lib.analysis.actions.CustomCommandLine.VectorArg;
import com.google.devtools.build.lib.cmdline.Label;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.errorprone.annotations.CompileTimeConstant;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.annotation.Nullable;

/**
 * Factory for functions to convert a {@code T} to a commandline argument. Uses a certain convention
 * for commandline arguments (e.g., separators, and ordering of container elements).
 */
public class AndroidDataConverter<T> extends ParametrizedMapFn<T> {

    /**
     * Converts Android data to the "SerializedAndroidData" format used by the Android data processing
     * actions.
     */
    static final AndroidDataConverter<MergableAndroidData> MERGABLE_DATA_CONVERTER = AndroidDataConverter
            .<MergableAndroidData>builder(JoinerType.SEMICOLON_AMPERSAND)
            .withRoots(MergableAndroidData::getResourceRoots).withRoots(MergableAndroidData::getAssetRoots)
            .withLabel(MergableAndroidData::getLabel).maybeWithArtifact(MergableAndroidData::getSymbols).build();

    /** Indicates the type of joiner between options expected by the command line. */
    public enum JoinerType {
        COLON_COMMA(":", ","), SEMICOLON_AMPERSAND(";", "&");

        private final String itemSeparator;
        private final String listSeparator;

        JoinerType(String itemSeparator, String listSeparator) {
            this.itemSeparator = itemSeparator;
            this.listSeparator = listSeparator;
        }

        private String escape(String string) {
            return string.replace(itemSeparator, "\\" + itemSeparator).replace(listSeparator, "\\" + listSeparator);
        }
    }

    private final ImmutableList<Function<T, String>> suppliers;
    private final JoinerType joinerType;

    private AndroidDataConverter(ImmutableList<Function<T, String>> suppliers, JoinerType joinerType) {
        this.suppliers = suppliers;
        this.joinerType = joinerType;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof AndroidDataConverter)) {
            return false;
        }

        AndroidDataConverter<?> other = (AndroidDataConverter) obj;
        return suppliers.equals(other.suppliers) && joinerType.equals(other.joinerType);
    }

    @Override
    public int hashCode() {
        return Objects.hash(suppliers, joinerType);
    }

    @Override
    public int maxInstancesAllowed() {
        // This is the max number of resource converters we expect to statically
        // construct for any given blaze instance.
        // Do not increase recklessly.
        return 10;
    }

    @Override
    public void expandToCommandLine(T t, Consumer<String> args) {
        args.accept(map(t));
    }

    public String map(T t) {
        return suppliers.stream().map(s -> (s.apply(t))).collect(Collectors.joining(joinerType.itemSeparator));
    }

    /**
     * Creates a builder for a new {@link AndroidDataConverter}.
     *
     * <p>Because of how Bazel handles these objects, call this method *only* as part of creating a
     * static final field.
     */
    public static <T> Builder<T> builder(JoinerType joinerType) {
        return new Builder<>(joinerType);
    }

    public VectorArg<String> getVectorArg(NestedSet<? extends T> values) {
        return VectorArg.join(joinerType.listSeparator).each(values).mapped(this);
    }

    public VectorArg<String> getVectorArgForEach(@CompileTimeConstant String arg, NestedSet<? extends T> values) {
        return VectorArg.addBefore(arg).each(values).mapped(this);
    }

    static class Builder<T> {
        private final ImmutableList.Builder<Function<T, String>> inner = ImmutableList.builder();
        private final JoinerType joinerType;

        private Builder(JoinerType joinerType) {
            this.joinerType = joinerType;
        }

        Builder<T> withRoots(Function<T, ImmutableList<PathFragment>> rootsFunction) {
            return with(t -> rootsToString(rootsFunction.apply(t)));
        }

        Builder<T> withArtifact(Function<T, Artifact> artifactFunction) {
            return with(t -> artifactFunction.apply(t).getExecPathString());
        }

        Builder<T> maybeWithArtifact(Function<T, Artifact> nullableArtifactFunction) {
            return with(t -> {
                @Nullable
                Artifact artifact = nullableArtifactFunction.apply(t);
                return artifact == null ? "" : artifact.getExecPathString();
            });
        }

        Builder<T> withLabel(Function<T, Label> labelFunction) {
            // Escape labels, since they are known to contain separating characters (specifically, ':').
            return with(t -> joinerType.escape(labelFunction.apply(t).toString()));
        }

        Builder<T> with(Function<T, String> stringFunction) {
            inner.add(stringFunction);
            return this;
        }

        AndroidDataConverter<T> build() {
            return new AndroidDataConverter<>(inner.build(), joinerType);
        }
    }

    static String rootsToString(ImmutableList<PathFragment> roots) {
        return roots.stream().map(PathFragment::toString).collect(Collectors.joining("#"));
    }
}