de.claas.parser.visitors.NodeEquality Maven / Gradle / Ivy
package de.claas.parser.visitors;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import de.claas.parser.Node;
import de.claas.parser.NodeVisitor;
import de.claas.parser.Rule;
import de.claas.parser.results.IntermediateNode;
import de.claas.parser.results.NonTerminalNode;
import de.claas.parser.results.TerminalNode;
/**
* The class {@link NodeEquality}. It is an implementation of the interface
* {@link NodeVisitor}. It is intended to compare a {@link Node}-hierarchy with
* a reference object.
*
* This visitor is meant for one-time use, only. As such, every comparison needs
* to be done with a separate instance of this visitor. An instance of this
* visitor should not be used to compare multiple {@link Rule}s.
*
* @author Claas Ahlrichs
*/
public class NodeEquality implements NodeVisitor {
private final Set visitedPath = new HashSet<>();
private Object obj;
private boolean visited = false;
private boolean equality = true;
/**
* Constructs a new {@link NodeEquality} with the specified parameter.
*
* @param obj
* the reference object with which the visited {@link Node}s are
* compared
*/
public NodeEquality(Object obj) {
this.obj = obj;
}
@Override
public void visitTerminalNode(TerminalNode node) {
markAsVisited();
if (preliminaryComparison(node, this.obj))
return;
TerminalNode other = (TerminalNode) this.obj;
if (isUnequal(node.getTerminal(), other.getTerminal()))
return; // already marked as unequal
}
@Override
public void visitIntermediateNode(IntermediateNode node) {
markAsVisited();
if (preliminaryComparison(node, this.obj))
return;
IntermediateNode other = (IntermediateNode) this.obj;
Integer uniqueId = new Integer(System.identityHashCode(node));
if (this.visitedPath.add(uniqueId)) {
visitChildren(node, other);
this.visitedPath.remove(uniqueId);
}
}
@Override
public void visitNonTerminaNode(NonTerminalNode node) {
markAsVisited();
if (preliminaryComparison(node, this.obj))
return;
NonTerminalNode other = (NonTerminalNode) this.obj;
if (isUnequal(node.getName(), other.getName()))
return; // already marked as unequal
Integer uniqueId = new Integer(System.identityHashCode(node));
if (this.visitedPath.add(uniqueId)) {
visitChildren(node, other);
this.visitedPath.remove(uniqueId);
}
}
/**
* Marks the two rules as unequal.
*/
private void markAsUnequal() {
this.equality = false;
}
/**
* Marks this visitor as visited. By default it is assumed that two nodes
* are equal, unless proven otherwise. However, this assumption requires the
* visitor to be visited (otherwise any two nodes would be assumed to be
* equal).
*/
private void markAsVisited() {
this.visited = true;
}
/**
* Returns true
if the two objects can already be said to be
* equal (or unequal). Otherwise, false
is returned.
*
* Side effect: this method may call {@link #markAsUnequal()}
*
* @param node
* the original node
* @param other
* the reference node with which the original node is compared
* @return true
if the two object can already be said to be
* equal (or unequal). Otherwise, false
is returned
*/
private boolean preliminaryComparison(Node node, Object other) {
if (node == other)
return true; // "this.equality" is already "true"
if (other == null || node.getClass() != other.getClass()) {
markAsUnequal();
return true;
}
return false;
}
/**
* Returns true
if the two objects can be said to be unequal.
* Otherwise, false
is returned.
*
* Side effect: this method may call {@link #markAsUnequal()}
*
* @param original
* the original object (e.g. name of node, terminal symbol, etc.)
* @param other
* the reference object with which the original object is
* compared
* @return true
if the two objects can be said to be unequal.
* Otherwise, false
is returned.
*/
private boolean isUnequal(Object original, Object other) {
if (original == null) {
if (other != null) {
markAsUnequal();
return true;
}
} else if (!original.equals(other)) {
markAsUnequal();
return true;
}
return false;
}
/**
* A helper function that successively visits all children of both specified
* {@link Node}s. It is main purpose is to ensure that the order in which
* the children occur is identical and that the children themselves are
* equal as well.
*
* @param node
* the original node
* @param other
* the reference node with which the original node is compared
*/
private void visitChildren(Node node, Node other) {
Iterator children = node.iterator();
Iterator otherChildren = other.iterator();
while (children.hasNext() && otherChildren.hasNext()) {
Node child = children.next();
this.obj = otherChildren.next();
child.visit(this);
if (!this.equality)
return;
}
this.equality = children.hasNext() == otherChildren.hasNext();
}
/**
* Returns true
if the visited nodes represent the same object
* that was passed into the constructor of this visitor. Otherwise
* false
is returned.
*
* @return true
if the visited nodes represent the same object
* that was passed into the constructor of this visitor,
* false
other
*/
public boolean isEquality() {
return this.equality && this.visited;
}
}