// Copyright 2007 Google Inc. All Rights Reserved. package com.google.caja.parser; /** * A lightweight stack that helps us keep track of ancestors as we traverse a * parse tree. * * @author mikesamuel@gmail.com (Mike Samuel) */ public final class AncestorChain<T extends ParseTreeNode> { public final AncestorChain<? extends ParseTreeNode> parent; public final T node; public final int depth; private AncestorChain(AncestorChain<? extends ParseTreeNode> parent, T node) { if (node == null) { throw new NullPointerException(); } assert parent == null || parent.node.children().contains(node); this.parent = parent; this.node = node; this.depth = parent == null ? 0 : parent.depth + 1; } public static <T extends ParseTreeNode> AncestorChain<T> instance(T node) { return new AncestorChain<T>(null, node); } public static <T extends ParseTreeNode> AncestorChain<T> instance( AncestorChain<? extends ParseTreeNode> parent, T node) { return new AncestorChain<T>(parent, node); } public <C extends ParseTreeNode> AncestorChain<C> child(C child) { return instance(this, child); } public ParseTreeNode getParentNode() { return parent != null ? parent.node : null; } @SuppressWarnings("unchecked") public <C extends ParseTreeNode> AncestorChain<C> cast(Class<C> clazz) { if (!clazz.isInstance(node)) { throw new ClassCastException(node.getClass().getSimpleName()); } return (AncestorChain<C>) this; } @Override public String toString() { StringBuilder sb = new StringBuilder(); toString(sb); return sb.toString(); } private int toString(StringBuilder sb) { int depth = 0; if (parent != null) { depth = parent.toString(sb) + 1; sb.append('\n'); } for (int d = depth; --d >= 0;) { sb.append(" "); } sb.append(node); return depth; } @Override public boolean equals(Object o) { if (!(o instanceof AncestorChain<?>)) { return false; } AncestorChain<?> a = this, b = (AncestorChain<?>) o; if (a.depth != b.depth) { return false; } do { if (a.node != b.node) { return false; } if (a.hc != 0 && b.hc != 0 && a.hc != b.hc) { return false; } a = a.parent; b = b.parent; if (a == b) { return true; } } while (a != null && b != null); return false; } private int hc; @Override public int hashCode() { if (this.hc == 0) { int hc = (parent != null ? 31 * parent.hashCode() : 0) + System.identityHashCode(node); if (hc == 0) { hc = -1; } this.hc = hc; } return this.hc; } }