@@ -145,9 +145,9 @@ public final class Node<V> {
       for (Map.Entry<Long, Long> entry : idToParent.entrySet()) {
         Node<V> node = idToNode.get(entry.getKey());
         Node<V> parent = idToNode.get(entry.getValue());
-        if (parent == null && rootNode == null) { // handle headless trace
-          rootNode = node;
-        } else if (parent == null) { // attribute missing parents to root
+        if (parent == null) { // handle headless trace
+          if (rootNode == null)
+            rootNode = new Node<>();
           rootNode.addChild(node);
         } else {
           parent.addChild(node);
/**
 * Copyright 2015-2017 The OpenZipkin Authors
 *
 * 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 zipkin.internal;

import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import zipkin.Span;

/**
 * Convenience type representing a tree. This is here because multiple facets in zipkin require
 * traversing the trace tree. For example, looking at network boundaries to correct clock skew, or
 * counting requests imply visiting the tree.
 *
 * @param <V> the node's value. Ex a full span or a tuple like {@code (serviceName, isLocal)}
 */
public final class Node<V> {

  /** Set via {@link #addChild(Node)} */
  private Node<V> parent;
  /** mutable as some transformations, such as clock skew, adjust this. */
  private V value;
  /** mutable to avoid allocating lists for childless nodes */
  private List<Node<V>> children = Collections.emptyList();

  /** Returns the parent, or null if root */
  @Nullable
  public Node<V> parent() {
    return parent;
  }

  public V value() {
    return value;
  }

  public Node<V> value(V newValue) {
    this.value = newValue;
    return this;
  }

  public Node<V> addChild(Node<V> child) {
    child.parent = this;
    if (children.equals(Collections.emptyList())) children = new LinkedList<>();
    children.add(child);
    return this;
  }

  /** Returns the children of this node. */
  public Collection<Node<V>> children() {
    return children;
  }

  /** Traverses the tree, breadth-first. */
  public Iterator<Node<V>> traverse() {
    return new BreadthFirstIterator<>(this);
  }

  static final class BreadthFirstIterator<V> implements Iterator<Node<V>> {
    private final Queue<Node<V>> queue = new ArrayDeque<>();

    BreadthFirstIterator(Node<V> root) {
      queue.add(root);
    }

    @Override
    public boolean hasNext() {
      return !queue.isEmpty();
    }

    @Override
    public Node<V> next() {
      Node<V> result = queue.remove();
      queue.addAll(result.children);
      return result;
    }

    @Override
    public void remove() {
      throw new UnsupportedOperationException("remove");
    }
  }

  /**
   * @param trace spans that belong to the same {@link Span#traceId trace}, in any order.
   */
  static Node<Span> constructTree(List<Span> trace) {
    TreeBuilder<Span> treeBuilder = new TreeBuilder<>();
    for (Span s : trace) {
      treeBuilder.addNode(s.parentId, s.id, s);
    }
    return treeBuilder.build();
  }

  /**
   * Some operations do not require the entire span object. This creates a tree given (parent id,
   * id) pairs.
   *
   * @param <V> same type as {@link Node#value}
   */
  public static final class TreeBuilder<V> {
    Node<V> rootNode = null;

    // Nodes representing the trace tree
    Map<Long, Node<V>> idToNode = new LinkedHashMap<>();
    // Collect the parent-child relationships between all spans.
    Map<Long, Long> idToParent = new LinkedHashMap<>(idToNode.size());

    public void addNode(Long parentId, long id, @Nullable V value) {
      Node<V> node = new Node<V>().value(value);
      if (parentId == null) {
        // special-case root, and attribute missing parents to it. In
        // other words, assume that the first root is the "real" root.
        if (rootNode == null) {
          rootNode = node;
        } else {
          idToNode.put(id, node);
          idToParent.put(id, null);
        }
      } else {
        idToNode.put(id, node);
        idToParent.put(id, parentId);
      }
    }

    /** Builds a tree from calls to {@link #addNode}, or returns an empty tree. */
    public Node<V> build() {
      // Materialize the tree using parent - child relationships
      for (Map.Entry<Long, Long> entry : idToParent.entrySet()) {
        Node<V> node = idToNode.get(entry.getKey());
        Node<V> parent = idToNode.get(entry.getValue());
        if (parent == null && rootNode == null) { // handle headless trace
          rootNode = node;
        } else if (parent == null) { // attribute missing parents to root
          rootNode.addChild(node);
        } else {
          parent.addChild(node);
        }
      }
      return rootNode != null ? rootNode : new Node<>();
    }
  }
}

rootNode = Node.MISSING_ROOT ^^ something like this makes the code more obvious, plus you can search references for Node.MISSING_ROOT, where you can't search references for null