com.facebook.buck.rules.macros.MacroFinder.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.rules.macros.MacroFinder.java

Source

/*
 * Copyright 2015-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.rules.macros;

import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;

import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Replace and extracts macros from input strings.
 *
 * Examples:
 *   $(exe //foo:bar)
 *   $(location :bar)
 *   $(platform)
 */
public class MacroFinder {

    /**
     * Matches a some macro name wrapped in <tt>$()</tt> followed by an optional argument, unless the
     * <code>$</code> is preceded by a backslash.
     * <p>
     * Given the input: $(exe //foo:bar), capturing groups are
     * <ol>
     *     <li> $(exe //foo:bar)
     *     <li> exe
     *     <li> //foo:bar
     * </ol>
     * <p>
     * If we match against $(platform), the capturing groups are:
     * <ol>
     *     <li> $(platform)
     *     <li> platform
     * </ol>
     **/
    private static final Pattern MACRO_PATTERN = Pattern.compile("\\$\\(([^)\\s]+)(?: ([^)]*))?\\)");

    public String replace(ImmutableMap<String, MacroReplacer> replacers, String blob) throws MacroException {

        StringBuilder expanded = new StringBuilder();

        // Iterate over all macros found in the string, expanding each found macro.
        int lastEnd = 0;
        Matcher matcher = MACRO_PATTERN.matcher(blob);
        while (matcher.find()) {

            // If the match is preceded by a backslash, skip this match but drop the backslash.
            if (matcher.start() > 0 && blob.charAt(matcher.start() - 1) == '\\') {
                expanded.append(blob.substring(lastEnd, matcher.start() - 1));
                expanded.append(blob.substring(matcher.start(), matcher.end()));

                // Otherwise we need to add the expanded value.
            } else {

                // Add everything from the original string since the last match to this one.
                expanded.append(blob.substring(lastEnd, matcher.start()));

                // Call the relevant expander and add the expanded value to the string.
                String name = matcher.group(1);
                MacroReplacer replacer = replacers.get(name);
                if (replacer == null) {
                    throw new MacroException(
                            String.format("expanding %s: no such macro \"%s\"", matcher.group(), name));
                }

                String input = Optional.fromNullable(matcher.group(2)).or("");
                try {
                    expanded.append(replacer.replace(input));
                } catch (MacroException e) {
                    throw new MacroException(String.format("expanding %s: %s", matcher.group(), e.getMessage()), e);
                }
            }

            lastEnd = matcher.end();
        }

        // Append the remaining part of the original string after the last match.
        expanded.append(blob.substring(lastEnd, blob.length()));

        return expanded.toString();
    }

    public ImmutableList<MacroMatchResult> findAll(ImmutableSet<String> macros, String blob) throws MacroException {

        ImmutableList.Builder<MacroMatchResult> results = ImmutableList.builder();

        // Iterate over all macros found in the string, and return a list of MacroMatchResults.
        Matcher matcher = MACRO_PATTERN.matcher(blob);
        while (matcher.find()) {
            if (matcher.start() > 0 && blob.charAt(matcher.start() - 1) == '\\') {
                continue;
            }
            String name = matcher.group(1);
            if (!macros.contains(name)) {
                throw new MacroException(String.format("no such macro \"%s\"", name));
            }
            String input = Optional.fromNullable(matcher.group(2)).or("");
            MatchResult matchResult = matcher.toMatchResult();
            results.add(MacroMatchResult.builder().setMacroType(name).setMacroInput(input)
                    .setStartIndex(matchResult.start()).setEndIndex(matchResult.end()).build());
        }

        return results.build();
    }

}