com.google.devtools.build.lib.graph.Node.java Source code

Java tutorial

Introduction

Here is the source code for com.google.devtools.build.lib.graph.Node.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.graph;

import com.google.common.collect.Sets;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * <p>A generic directed-graph Node class.  Type parameter T is the type
 * of the node's label.
 *
 * <p>Each node is identified by a label, which is unique within the graph
 * owning the node.
 *
 * <p>Nodes are immutable, that is, their labels cannot be changed.  However,
 * their predecessor/successor lists are mutable.
 *
 * <p>Nodes cannot be created directly by clients.
 *
 * <p>Clients should not confuse nodes belonging to two different graphs!  (Use
 * Digraph.checkNode() to catch such errors.)  There is no way to find the
 * graph to which a node belongs; it is intentionally not represented, to save
 * space.
 */
public final class Node<T> {

    private static final int ARRAYLIST_THRESHOLD = 6;
    private static final int INITIAL_HASHSET_CAPACITY = 12;

    // The succs and preds set representation changes depending on its size.
    // It is implemented using the following collections:
    // - null for size = 0.
    // - Collections$SingletonList for size = 1.
    // - ArrayList(6) for size = [2..6].
    // - HashSet(12) for size > 6.
    // These numbers were chosen based on profiling.

    private final T label;

    /**
     * A duplicate-free collection of edges from this node.  May be null,
     * indicating the empty set.
     */
    private Collection<Node<T>> succs = null;

    /**
     * A duplicate-free collection of edges to this node.  May be null,
     * indicating the empty set.
     */
    private Collection<Node<T>> preds = null;

    private final int hashCode;

    /**
     * Only Digraph.createNode() can call this!
     */
    Node(T label, int hashCode) {
        if (label == null) {
            throw new NullPointerException("label");
        }
        this.label = label;
        this.hashCode = hashCode;
    }

    /**
     * Returns the label for this node.
     */
    public T getLabel() {
        return label;
    }

    /**
     * Returns a duplicate-free collection of the nodes that this node links to.
     */
    public Collection<Node<T>> getSuccessors() {
        if (succs == null) {
            return Collections.emptyList();
        } else {
            return Collections.unmodifiableCollection(succs);
        }
    }

    /**
     * Equivalent to {@code !getSuccessors().isEmpty()} but possibly more
     * efficient.
     */
    public boolean hasSuccessors() {
        return succs != null;
    }

    /**
     * Equivalent to {@code getSuccessors().size()} but possibly more efficient.
     */
    public int numSuccessors() {
        return succs == null ? 0 : succs.size();
    }

    /**
     * Removes all edges to/from this node.
     * Private: breaks graph invariant!
     */
    void removeAllEdges() {
        this.succs = null;
        this.preds = null;
    }

    /**
     * Returns an (unordered, possibly immutable) set of the nodes that link to
     * this node.
     */
    public Collection<Node<T>> getPredecessors() {
        if (preds == null) {
            return Collections.emptyList();
        } else {
            return Collections.unmodifiableCollection(preds);
        }
    }

    /**
     * Equivalent to {@code getPredecessors().size()} but possibly more
     * efficient.
     */
    public int numPredecessors() {
        return preds == null ? 0 : preds.size();
    }

    /**
     * Equivalent to {@code !getPredecessors().isEmpty()} but possibly more
     * efficient.
     */
    public boolean hasPredecessors() {
        return preds != null;
    }

    /**
     * Adds 'value' to either the predecessor or successor set, updating the
     * appropriate field as necessary.
     * @return {@code true} if the set was modified; {@code false} if the set
     * was not modified
     */
    private boolean add(boolean predecessorSet, Node<T> value) {
        final Collection<Node<T>> set = predecessorSet ? preds : succs;
        if (set == null) {
            // null -> SingletonList
            return updateField(predecessorSet, Collections.singletonList(value));
        }
        if (set.contains(value)) {
            // already exists in this set
            return false;
        }
        int previousSize = set.size();
        if (previousSize == 1) {
            // SingletonList -> ArrayList
            Collection<Node<T>> newSet = new ArrayList<>(ARRAYLIST_THRESHOLD);
            newSet.addAll(set);
            newSet.add(value);
            return updateField(predecessorSet, newSet);
        } else if (previousSize < ARRAYLIST_THRESHOLD) {
            // ArrayList
            set.add(value);
            return true;
        } else if (previousSize == ARRAYLIST_THRESHOLD) {
            // ArrayList -> HashSet
            Collection<Node<T>> newSet = Sets.newHashSetWithExpectedSize(INITIAL_HASHSET_CAPACITY);
            newSet.addAll(set);
            newSet.add(value);
            return updateField(predecessorSet, newSet);
        } else {
            // HashSet
            set.add(value);
            return true;
        }
    }

    /**
     * Removes 'value' from either 'preds' or 'succs', updating the appropriate
     * field as necessary.
     * @return {@code true} if the set was modified; {@code false} if the set
     * was not modified
     */
    private boolean remove(boolean predecessorSet, Node<T> value) {
        final Collection<Node<T>> set = predecessorSet ? preds : succs;
        if (set == null) {
            // null
            return false;
        }

        int previousSize = set.size();
        if (previousSize == 1) {
            if (set.contains(value)) {
                // -> null
                return updateField(predecessorSet, null);
            } else {
                return false;
            }
        }
        // now remove the value
        if (set.remove(value)) {
            // may need to change representation
            if (previousSize == 2) {
                // -> SingletonList
                List<Node<T>> list = Collections.singletonList(set.iterator().next());
                return updateField(predecessorSet, list);

            } else if (previousSize == 1 + ARRAYLIST_THRESHOLD) {
                // -> ArrayList
                Collection<Node<T>> newSet = new ArrayList<>(ARRAYLIST_THRESHOLD);
                newSet.addAll(set);
                return updateField(predecessorSet, newSet);
            }
            return true;
        }
        return false;
    }

    /**
     * Update either the {@link #preds} or {@link #succs} field to point to the
     * new set.
     * @return {@code true}, because the set must have been updated
     */
    private boolean updateField(boolean predecessorSet, Collection<Node<T>> newSet) {
        if (predecessorSet) {
            preds = newSet;
        } else {
            succs = newSet;
        }
        return true;
    }

    /**
     * Add 'to' as a successor of 'this' node.  Returns true iff
     * the graph changed.  Private: breaks graph invariant!
     */
    boolean addSuccessor(Node<T> to) {
        return add(false, to);
    }

    /**
     * Add 'from' as a predecessor of 'this' node.  Returns true iff
     * the graph changed.  Private: breaks graph invariant!
     */
    boolean addPredecessor(Node<T> from) {
        return add(true, from);
    }

    /**
     * Remove edge: fromNode.succs = {n | n in fromNode.succs && n != toNode}
     * Private: breaks graph invariant!
     */
    boolean removeSuccessor(Node<T> to) {
        return remove(false, to);
    }

    /**
     * Remove edge: toNode.preds = {n | n in toNode.preds && n != fromNode}
     * Private: breaks graph invariant!
     */
    boolean removePredecessor(Node<T> from) {
        return remove(true, from);
    }

    @Override
    public String toString() {
        return "node:" + label;
    }

    @Override
    public int hashCode() {
        return hashCode; // Fast, deterministic.
    }

    @Override
    public boolean equals(Object that) {
        return this == that; // Nodes are unique for a given label
    }
}