| // 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.base.Preconditions; | 
 | import java.util.Collection; | 
 |  | 
 | /** | 
 |  * 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. | 
 |  * | 
 |  * <p>During adding or removing edge locks always hold in specific order: first=nodeFrom.succs then | 
 |  * second=nodeTo.preds. That's why reordering deadlock never happens. | 
 |  */ | 
 | public final class Node<T> { | 
 |  | 
 |   private final T label; | 
 |  | 
 |   /** A duplicate-free collection of edges from this node. May be null, indicating the empty set. */ | 
 |   private final ConcurrentCollectionWrapper<Node<T>> succs = new ConcurrentCollectionWrapper<>(); | 
 |  | 
 |   /** A duplicate-free collection of edges to this node. May be null, indicating the empty set. */ | 
 |   private final ConcurrentCollectionWrapper<Node<T>> preds = new ConcurrentCollectionWrapper<>(); | 
 |  | 
 |   /** | 
 |    * Only Digraph.createNode() can call this! | 
 |    */ | 
 |   Node(T label) { | 
 |     this.label = Preconditions.checkNotNull(label, "label"); | 
 |   } | 
 |  | 
 |   /** | 
 |    * 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() { | 
 |     return this.succs.get(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Remove all successors edges and return collection of its. Self edge removed but did not | 
 |    * returned in result collection. | 
 |    * | 
 |    * @return all existed before successor nodes but this. | 
 |    */ | 
 |   Collection<Node<T>> removeAllSuccessors() { | 
 |     this.removeEdge(this); // remove self edge | 
 |     Collection<Node<T>> successors = this.succs.clear(); | 
 |     for (Node<T> s : successors) { | 
 |       if (!s.removePredecessor(this)) { | 
 |         throw new IllegalStateException("inconsistent graph state"); | 
 |       } | 
 |     } | 
 |     return successors; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Equivalent to {@code !getSuccessors().isEmpty()} but possibly more | 
 |    * efficient. | 
 |    */ | 
 |   public boolean hasSuccessors() { | 
 |     return !this.succs.get().isEmpty(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Equivalent to {@code getSuccessors().size()} but possibly more efficient. | 
 |    */ | 
 |   public int numSuccessors() { | 
 |     return this.succs.size(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Returns an (unordered, possibly immutable) set of the nodes that link to | 
 |    * this node. | 
 |    */ | 
 |   public Collection<Node<T>> getPredecessors() { | 
 |     return this.preds.get(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Remove all predecessors edges and return collection of its. Self edge removed but did not | 
 |    * returned in result collection. | 
 |    * | 
 |    * @return all existed before predecessor nodes but this. | 
 |    */ | 
 |   Collection<Node<T>> removeAllPredecessors() { | 
 |     this.removeEdge(this); // remove self edge | 
 |     Collection<Node<T>> predecessors = this.preds.clear(); | 
 |     for (Node<T> p : predecessors) { | 
 |       if (!p.removeSuccessor(this)) { | 
 |         throw new IllegalStateException("inconsistent graph state"); | 
 |       } | 
 |     } | 
 |     return predecessors; | 
 |   } | 
 |  | 
 |   /** | 
 |    * Equivalent to {@code !getPredecessors().isEmpty()} but possibly more | 
 |    * efficient. | 
 |    */ | 
 |   public boolean hasPredecessors() { | 
 |     return !preds.get().isEmpty(); | 
 |   } | 
 |  | 
 |   /** Equivalent to {@code getPredecessors().size()} but possibly more efficient. */ | 
 |   public int numPredecessors() { | 
 |     return this.preds.size(); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Adds edge from this node to target | 
 |    * | 
 |    * <p>In this method one lock held inside another lock. But it can not be reason of reordering | 
 |    * deadlock. Lock always holds in direction fromNode.succs -> toNode.preds. | 
 |    * @see #removeEdge(Node) | 
 |    * | 
 |    * @return true if edge had been added. false - otherwise. | 
 |    */ | 
 |   boolean addEdge(Node<T> target) { | 
 |     synchronized (succs) { | 
 |       boolean isNewSuccessor = this.succs.add(target); | 
 |       boolean isNewPredecessor = target.addPredecessor(this); | 
 |       if (isNewPredecessor != isNewSuccessor) { | 
 |         throw new IllegalStateException("inconsistent graph state"); | 
 |       } | 
 |       return isNewSuccessor; | 
 |     } | 
 |   } | 
 |  | 
 |   /** | 
 |    * Adds edge from this node to target | 
 |    * | 
 |    * <p>In this method one lock held inside another lock. But it can not be reason of reordering | 
 |    * deadlock. Lock always holds in direction fromNode.succs -> toNode.preds. | 
 |    * @see #addEdge(Node) | 
 |    * | 
 |    * @return true if edge had been removed. false - otherwise. | 
 |    */ | 
 |   boolean removeEdge(Node<T> target) { | 
 |     synchronized (succs) { | 
 |       boolean isSuccessorRemoved = this.succs.remove(target); | 
 |       if (isSuccessorRemoved) { | 
 |         boolean isPredecessorRemoved = target.removePredecessor(this); | 
 |         if (!isPredecessorRemoved) { | 
 |           throw new IllegalStateException("inconsistent graph state"); | 
 |         } | 
 |         return true; | 
 |       } | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   /** | 
 |    * Add 'from' as a predecessor of 'this' node. Returns true iff the graph changed. Private: breaks | 
 |    * graph invariant! | 
 |    */ | 
 |   private boolean addPredecessor(Node<T> from) { | 
 |     return preds.add(from); | 
 |   } | 
 |  | 
 |   /** | 
 |    * Remove edge: toNode.preds = {n | n in toNode.preds && n != fromNode} Private: breaks graph | 
 |    * invariant! | 
 |    */ | 
 |   private boolean removePredecessor(Node<T> from) { | 
 |     return preds.remove(from); | 
 |   } | 
 |  | 
 |   private boolean removeSuccessor(Node<T> to) { | 
 |     return succs.remove(to); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public String toString() { | 
 |     return "node:" + label; | 
 |   } | 
 |  | 
 |   @Override | 
 |   public int hashCode() { | 
 |     return super.hashCode(); | 
 |   } | 
 |  | 
 |   @Override | 
 |   public boolean equals(Object that) { | 
 |     return this == that; // Nodes are unique for a given label | 
 |   } | 
 | } |