com.facebook.buck.tools.consistency.RuleKeyFileParser.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.tools.consistency.RuleKeyFileParser.java

Source

/*
 * Copyright 2017-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.tools.consistency;

import com.facebook.buck.log.thrift.rulekeys.FullRuleKey;
import com.facebook.buck.tools.consistency.RuleKeyLogFileReader.ParseException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.nio.file.Path;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

/** A class that can parse out length prefixed, thrift compact encoded rule keys. */
public class RuleKeyFileParser {

    private final RuleKeyLogFileReader reader;

    public RuleKeyFileParser(RuleKeyLogFileReader reader) {
        this.reader = reader;
    }

    /**
     * A node in the set of all rule keys. When parsing, it tracks whether the node has been visited
     * yet or not.
     */
    static class RuleKeyNode {

        public final FullRuleKey ruleKey;
        public int lastVisitId;

        public RuleKeyNode(FullRuleKey ruleKey) {
            this.ruleKey = ruleKey;
        }

        public String toString() {
            return ruleKey.toString();
        }
    }

    /** A parsed rule key file with details of the parse, and all rules contained in the file. */
    static class ParsedRuleKeyFile {

        public final Path filename;
        public final ImmutableMap<String, RuleKeyNode> rootNodes;
        public final Map<String, RuleKeyNode> rules;
        public final Duration parseTime;

        public ParsedRuleKeyFile(Path filename, ImmutableMap<String, RuleKeyNode> rootNodes,
                Map<String, RuleKeyNode> rules, Duration parseTime) {
            this.filename = filename;
            this.rootNodes = rootNodes;
            this.rules = rules;
            this.parseTime = parseTime;
        }
    }

    /** Arbitrary estimate of an average rule key size when serialized */
    public final int THRIFT_STRUCT_SIZE = 300;

    /**
     * Parse a thrift compact serialized file
     *
     * @param filename The name of the file
     * @param targetNames The name of the targets that should be found
     * @return A {@link ParsedRuleKeyFile} object that all deserialized rules, and the rule key hash
     *     of the specified target
     * @throws ParseException If an IO or serialization error occurs, or if the target could not be
     *     found in the file
     */
    public ParsedRuleKeyFile parseFile(Path filename, ImmutableSet<String> targetNames) throws ParseException {
        // If //foo/bar/... is passed in, we want to find all targets that start with
        // //foo/bar, and that are of the right type, and add them as root nodes
        ImmutableList<String> recursiveTargetPrefixes = targetNames.stream()
                .filter(name -> name.endsWith("/...") || name.endsWith(":")).map(name -> {
                    int idx = name.lastIndexOf("/...");
                    if (idx != -1) {
                        return name.substring(0, idx);
                    } else {
                        return name;
                    }
                }).collect(ImmutableList.toImmutableList());
        long startNanos = System.nanoTime();
        int initialSize;
        try {
            initialSize = Math.toIntExact(filename.toFile().length() / THRIFT_STRUCT_SIZE);
        } catch (ArithmeticException e) {
            throw new ParseException(e, filename,
                    "File size is too large (>2.1 billion objects would be deserialized");
        }

        ImmutableMap.Builder<String, RuleKeyNode> rootNodesBuilder = ImmutableMap.builder();
        Map<String, RuleKeyNode> rules = new HashMap<>();

        try {
            reader.readFile(filename, ruleKey -> {
                RuleKeyNode newNode = new RuleKeyNode(ruleKey);
                if ("DEFAULT".equals(ruleKey.type)) {
                    // If either a specific rule is present, or if the target starts with one of the
                    // prefixes
                    if (targetNames.contains(ruleKey.name) || recursiveTargetPrefixes.stream()
                            .filter(prefix -> ruleKey.name.startsWith(prefix)).findFirst().isPresent()) {
                        rootNodesBuilder.put(ruleKey.name, newNode);
                    }
                }
                RuleKeyNode oldValue = rules.put(ruleKey.key, newNode);
                if (oldValue != null && !oldValue.ruleKey.equals(newNode.ruleKey)) {
                    throw new RuntimeException(new ParseException(filename,
                            "Found two rules with the same key, but different values. Key: %s, first value: "
                                    + "%s, second value: %s",
                            ruleKey.key, oldValue.ruleKey, newNode.ruleKey));
                }
                return false;
            });
        } catch (RuntimeException e) {
            if (e.getCause() instanceof ParseException) {
                throw (ParseException) e.getCause();
            }
        }

        ImmutableMap<String, RuleKeyNode> rootNodes = rootNodesBuilder.build();
        for (String targetName : targetNames) {
            if (!targetName.endsWith("/...") && !targetName.endsWith(":") && !rootNodes.containsKey(targetName)) {
                throw new ParseException(filename, "Could not find %s in %s", targetName, filename);
            }
        }
        Duration runtime = Duration.ofNanos(System.nanoTime() - startNanos);
        return new ParsedRuleKeyFile(filename, rootNodes, rules, runtime);
    }
}