// 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.collect.nestedset; import com.google.common.base.Preconditions; import com.google.common.collect.Sets; import java.util.Set; /** * NestedSetVisitor facilitates a transitive visitation over a NestedSet, which must be in STABLE * order. The callback may be called from multiple threads, and must be thread-safe. * *

The visitation is iterative: The caller may invoke a NestedSet within the top-level NestedSet * in any order. * *

Currently this class is only used in Skyframe to facilitate iterative replay of transitive * warnings/errors. * * @param the data type */ // @ThreadSafety.ThreadSafe public final class NestedSetVisitor { /** * For each element of the NestedSet the {@code Receiver} will receive one element during the * visitation. */ public interface Receiver { void accept(E arg); } private final Receiver callback; private final VisitedState visited; public NestedSetVisitor(Receiver callback, VisitedState visited) { this.callback = Preconditions.checkNotNull(callback); this.visited = Preconditions.checkNotNull(visited); } /** * Transitively visit a nested set. * * @param nestedSet the nested set to visit transitively. */ public void visit(NestedSet nestedSet) { Preconditions.checkArgument(nestedSet.getOrder() == Order.STABLE_ORDER); // We can short-circuit empty nested set visitation here, avoiding load on the shared map // VisitedState#seenNodes. if (!nestedSet.isEmpty()) { visitRaw(nestedSet.getChildren()); } } @SuppressWarnings("unchecked") private void visitRaw(Object node) { if (visited.add(node)) { if (node instanceof Object[]) { for (Object child : (Object[]) node) { visitRaw(child); } } else { callback.accept((E) node); } } } /** A class that allows us to keep track of the seen nodes and transitive sets. */ public static class VisitedState { private final Set seenNodes = Sets.newConcurrentHashSet(); public void clear() { seenNodes.clear(); } private boolean add(Object node) { // Though it may look redundant, the contains call is much cheaper than the add and can // greatly improve the performance and reduce the contention associated with checking // seenNodes. return !seenNodes.contains(node) && seenNodes.add(node); } } }