com.google.devtools.build.lib.syntax.GlobCriteria.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.syntax.GlobCriteria.java

Source

// Copyright 2014 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.syntax;

import com.google.common.base.Functions;
import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.Iterables;
import com.google.devtools.build.lib.util.Preconditions;

import java.util.List;

import javax.annotation.Nullable;

/**
 * Either the arguments to a glob call (the include and exclude lists) or the
 * contents of a fixed list that was appended to a list of glob results.
 * (The latter need to be stored by {@link GlobList} in order to fully
 * reproduce the inputs that created the output list.)
 *
 * <p>For example, the expression
 * <code>glob(['*.java']) + ['x.properties']</code>
 * will result in two GlobCriteria: one has include = ['*.java'], glob = true
 * and the other, include = ['x.properties'], glob = false.
 */
public class GlobCriteria {

    /**
     * A list of names or patterns that are included by this glob. They should
     * consist of characters that are valid in labels in the BUILD language.
     */
    private final ImmutableList<String> include;

    /**
     * A list of names or patterns that are excluded by this glob. They should
     * consist of characters that are valid in labels in the BUILD language.
     */
    private final ImmutableList<String> exclude;

    /** True if the includes list was passed to glob(), false if not. */
    private final boolean glob;

    /**
     * Parses criteria from its {@link #toExpression} form.
     * Package-private for use by tests and GlobList.
     * @throws IllegalArgumentException if the expression cannot be parsed
     */
    public static GlobCriteria parse(String text) {
        if (text.startsWith("glob([") && text.endsWith("])")) {
            int excludeIndex = text.indexOf("], exclude=[");
            if (excludeIndex == -1) {
                String listText = text.substring(6, text.length() - 2);
                return new GlobCriteria(parseList(listText), ImmutableList.<String>of(), true);
            } else {
                String listText = text.substring(6, excludeIndex);
                String excludeText = text.substring(excludeIndex + 12, text.length() - 2);
                return new GlobCriteria(parseList(listText), parseList(excludeText), true);
            }
        } else if (text.startsWith("[") && text.endsWith("]")) {
            String listText = text.substring(1, text.length() - 1);
            return new GlobCriteria(parseList(listText), ImmutableList.<String>of(), false);
        } else {
            throw new IllegalArgumentException("unrecognized format (not from toExpression?): " + text);
        }
    }

    /**
     * Constructs a copy of a given glob critera object, with additional exclude patterns added.
     *
     * @param base a glob criteria object to copy. Must be an actual glob
     * @param excludes a list of pattern strings indicating new excludes to provide
     * @return a new glob criteria object which contains the same parameters as {@code base}, with
     *   the additional patterns in {@code excludes} added.
     * @throws IllegalArgumentException if {@code base} is not a glob
     */
    public static GlobCriteria createWithAdditionalExcludes(GlobCriteria base, List<String> excludes) {
        Preconditions.checkArgument(base.isGlob());
        return fromGlobCall(base.include, ImmutableList.copyOf(Iterables.concat(base.exclude, excludes)));
    }

    /**
     * Constructs a copy of a fixed list, converted to Strings.
     */
    public static GlobCriteria fromList(Iterable<?> list) {
        Iterable<String> strings = Iterables.transform(list, Functions.toStringFunction());
        return new GlobCriteria(ImmutableList.copyOf(strings), ImmutableList.<String>of(), false);
    }

    /**
     * Constructs a glob call with include and exclude list.
     *
     * @param include list of included patterns
     * @param exclude list of excluded patterns
     */
    public static GlobCriteria fromGlobCall(ImmutableList<String> include, ImmutableList<String> exclude) {
        return new GlobCriteria(include, exclude, true);
    }

    /**
     * Constructs a glob call with include and exclude list.
     */
    private GlobCriteria(ImmutableList<String> include, ImmutableList<String> exclude, boolean glob) {
        this.include = include;
        this.exclude = exclude;
        this.glob = glob;
    }

    /**
     * Returns the patterns that were included in this {@code glob()} call.
     */
    public ImmutableList<String> getIncludePatterns() {
        return include;
    }

    /**
     * Returns the patterns that were excluded in this {@code glob()} call.
     */
    public ImmutableList<String> getExcludePatterns() {
        return exclude;
    }

    /**
     * Returns true if the include list was passed to {@code glob()}, false
     * if it was a fixed list. If this returns false, the exclude list will
     * always be empty.
     */
    public boolean isGlob() {
        return glob;
    }

    /**
     * Returns a String that represents this glob as a BUILD expression.
     * For example, <code>glob(['abc', 'def'], exclude=['uvw', 'xyz'])</code>
     * or <code>['foo', 'bar', 'baz']</code>.
     */
    public String toExpression() {
        StringBuilder sb = new StringBuilder();
        if (glob) {
            sb.append("glob(");
        }
        sb.append('[');
        appendList(sb, include);
        if (!exclude.isEmpty()) {
            sb.append("], exclude=[");
            appendList(sb, exclude);
        }
        sb.append(']');
        if (glob) {
            sb.append(')');
        }
        return sb.toString();
    }

    @Override
    public String toString() {
        return toExpression();
    }

    /**
     * Takes a list of Strings, quotes them in single quotes, and appends them to
     * a StringBuilder separated by a comma and space. This can be parsed back
     * out by {@link #parseList}.
     */
    private static void appendList(StringBuilder sb, List<String> list) {
        boolean first = true;
        for (String content : list) {
            if (!first) {
                sb.append(", ");
            }
            sb.append('\'').append(content).append('\'');
            first = false;
        }
    }

    /**
     * Takes a String in the format created by {@link #appendList} and returns
     * the original Strings. A null String (which may be returned when Pattern
     * does not find a match) or the String "" (which will be captured in "[]")
     * will result in an empty list.
     */
    private static ImmutableList<String> parseList(@Nullable String text) {
        if (text == null) {
            return ImmutableList.of();
        }
        Iterable<String> split = Splitter.on(", ").split(text);
        Builder<String> listBuilder = ImmutableList.builder();
        for (String element : split) {
            if (!element.isEmpty()) {
                if ((element.length() < 2) || !element.startsWith("'") || !element.endsWith("'")) {
                    throw new IllegalArgumentException("expected a filename or pattern in quotes: " + text);
                }
                listBuilder.add(element.substring(1, element.length() - 1));
            }
        }
        return listBuilder.build();
    }
}