package sk.stuba.fiit.perconik.core.java.dom;
import java.util.List;
import javax.annotation.Nullable;
import com.google.common.base.Optional;
import org.eclipse.jdt.core.dom.ASTMatcher;
import org.eclipse.jdt.core.dom.ASTNode;
import sk.stuba.fiit.perconik.eclipse.jdt.core.dom.NodeType;
import static com.google.common.base.Optional.fromNullable;
import static sk.stuba.fiit.perconik.core.java.dom.Nodes.toType;
import static sk.stuba.fiit.perconik.utilities.MoreLists.newArrayListSuitableFor;
public final class MatchingNode<N extends ASTNode> {
private static final ASTMatcher matcher = new ASTMatcher(true);
@Nullable
private final N node;
private volatile int hash;
private MatchingNode(@Nullable final N node) {
this.node = node;
}
public static <N extends ASTNode> MatchingNode<N> of(@Nullable final N node) {
return new MatchingNode<>(node);
}
public static <N extends ASTNode> List<MatchingNode<N>> wrap(final Iterable<? extends N> nodes) {
List<MatchingNode<N>> wrapped = newArrayListSuitableFor(nodes);
for (N node: nodes) {
wrapped.add(of(node));
}
return wrapped;
}
public static <N extends ASTNode> List<N> unwrap(final Iterable<? extends MatchingNode<N>> nodes) {
List<N> unwrapped = newArrayListSuitableFor(nodes);
for (MatchingNode<? extends N> node: nodes) {
unwrapped.add(node.node);
}
return unwrapped;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MatchingNode)) {
return false;
}
MatchingNode<?> other = (MatchingNode<?>) o;
return matcher.safeSubtreeMatch(this.node, other.node);
}
@Override
public int hashCode() {
int hash = this.hash;
if (hash == 0 && this.node != null) {
synchronized (this) {
hash = this.hash;
if (hash == 0) {
hash = this.hash = this.node.toString().hashCode();
}
}
}
return hash;
}
@Override
public String toString() {
return this.node != null ? this.node.toString() : null;
}
@Nullable
public N asNode() {
return this.node;
}
public Optional<N> asOption() {
return fromNullable(this.node);
}
public MatchingNode<?> getRoot() {
return of(Nodes.root(this.node));
}
public MatchingNode<?> getParent() {
return of(Nodes.parent(this.node));
}
public NodeType getType() {
return toType(this.node);
}
}