/**
   * Generic (model-independent) method to determine the relative position of two node in document
   * order. The nodes must be in the same tree.
   *
   * @param first The first node
   * @param second The second node, whose position is to be compared with the first node
   * @return -1 if this node precedes the other node, +1 if it follows the other node, or 0 if they
   *     are the same node. (In this case, isSameNode() will always return true, and the two nodes
   *     will produce the same result for generateId())
   */
  public static int compareOrder(SiblingCountingNode first, SiblingCountingNode second) {
    NodeInfo ow = second;

    // are they the same node?
    if (first.isSameNode(second)) {
      return 0;
    }
    // are they siblings (common case)
    if (first.getParent().isSameNode(second.getParent())) {
      return first.getSiblingPosition() - second.getSiblingPosition();
    }
    // find the depths of both nodes in the tree

    int depth1 = 0;
    int depth2 = 0;
    NodeInfo p1 = first;
    NodeInfo p2 = second;
    while (p1 != null) {
      depth1++;
      p1 = p1.getParent();
    }
    while (p2 != null) {
      depth2++;
      p2 = p2.getParent();
    }
    // move up one branch of the tree so we have two nodes on the same level

    p1 = first;
    while (depth1 > depth2) {
      p1 = p1.getParent();
      if (p1.isSameNode(second)) {
        return +1;
      }
      depth1--;
    }

    p2 = ow;
    while (depth2 > depth1) {
      p2 = p2.getParent();
      if (p2.isSameNode(first)) {
        return -1;
      }
      depth2--;
    }

    // now move up both branches in sync until we find a common parent
    while (true) {
      NodeInfo par1 = p1.getParent();
      NodeInfo par2 = p2.getParent();
      if (par1 == null || par2 == null) {
        throw new NullPointerException("DOM tree compare - internal error");
      }
      if (par1.isSameNode(par2)) {
        return ((SiblingCountingNode) p1).getSiblingPosition()
            - ((SiblingCountingNode) p2).getSiblingPosition();
      }
      p1 = par1;
      p2 = par2;
    }
  }
 /**
  * Get a character string that uniquely identifies this node and that collates nodes into document
  * order
  *
  * @return a string. The string is always interned so keys can be compared using "==".
  */
 public static String getSequentialKey(SiblingCountingNode node) {
   // TODO: this was designed so it could be used for sorting nodes into document
   // order, but is not currently used that way.
   StringBuffer key = new StringBuffer();
   while (!(node instanceof DocumentInfo)) {
     key.insert(0, alphaKey(node.getSiblingPosition()));
     node = (SiblingCountingNode) node.getParent();
   }
   key.insert(0, "w" + node.getDocumentNumber());
   return key.toString().intern();
 }