/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.dfa.report;
import java.util.Iterator;
import net.sourceforge.pmd.RuleViolation;
public class ReportTree implements Iterable<RuleViolation> {
private PackageNode rootNode = new PackageNode("");
private AbstractReportNode level;
private class TreeIterator implements Iterator<RuleViolation> {
private AbstractReportNode iterNode = rootNode;
private boolean hasNextFlag;
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
hasNextFlag = true;
return getNext() != null;
}
@Override
public RuleViolation next() {
if (!hasNextFlag) {
getNext();
} else {
hasNextFlag = false;
}
if (iterNode instanceof ViolationNode) {
return ((ViolationNode) iterNode).getRuleViolation();
}
return null;
}
/**
* It's some kind of left-right-middle search (postorder). It always
* returns only leafs. The first node he returns is the most left handed
* leaf he can found. Now he's looking for siblings and if there are
* any, he starts searching for the next most left handed leaf. If there
* are no siblings he goes up to his parent and starts looking for
* siblings. If there are any he starts searching for the next most left
* handed leaf again. And so on ... until he wants to get the parent of
* the root node. Because there is no one, the search stops.
*/
private AbstractReportNode getNext() {
AbstractReportNode node;
while (true) {
if (iterNode.isLeaf()) {
while ((node = iterNode.getNextSibling()) == null) {
node = iterNode.getParent();
if (node == null) {
return null;
} else {
iterNode = node;
}
}
iterNode = node;
if (iterNode.isLeaf()) {
return iterNode;
} else {
continue;
}
} else {
iterNode = iterNode.getFirstChild();
if (iterNode.isLeaf()) {
return iterNode;
} else {
continue;
}
}
}
}
}
@Override
public Iterator<RuleViolation> iterator() {
return new TreeIterator();
}
public int size() {
int count = 0;
for (Iterator<RuleViolation> i = iterator(); i.hasNext();) {
i.next();
count++;
}
return count;
}
public AbstractReportNode getRootNode() {
return rootNode;
}
/**
* Adds the RuleViolation to the tree. Splits the package name. Each
* package, class and violation gets there own tree node.
*/
public void addRuleViolation(RuleViolation violation) {
String packageName = violation.getPackageName();
if (packageName == null) {
packageName = "";
}
level = rootNode;
int endIndex = packageName.indexOf('.');
while (true) {
String parentPackage;
if (endIndex < 0) {
parentPackage = packageName;
} else {
parentPackage = packageName.substring(0, endIndex);
}
if (!isStringInLevel(parentPackage)) {
PackageNode node = new PackageNode(parentPackage);
level.addFirst(node);
// gotoLevel
level = node;
}
if (endIndex < 0) {
break;
}
endIndex = packageName.indexOf('.', endIndex + 1);
}
String cl = violation.getClassName();
if (!isStringInLevel(cl)) {
ClassNode node = new ClassNode(cl);
level.addFirst(node);
// gotoLevel
level = node;
}
/*
* Filters duplicated rule violations. Like the comparator in
* RuleViolation if he already exists.
*/
ViolationNode tmp = new ViolationNode(violation);
if (!equalsNodeInLevel(level, tmp)) {
level.add(tmp);
}
}
/**
* Checks if node is a child of the level node.
*/
private boolean equalsNodeInLevel(AbstractReportNode level, AbstractReportNode node) {
for (int i = 0; i < level.getChildCount(); i++) {
if (level.getChildAt(i).equalsNode(node)) {
return true;
}
}
return false;
}
/**
* Checks if the packageName or the className is a child of the current
* (this.level) node. If it's true, the current node changes to the child
* node.
*/
private boolean isStringInLevel(String str) {
for (int i = 0; i < level.getChildCount(); i++) {
final AbstractReportNode child = level.getChildAt(i);
final String tmp;
if (child instanceof PackageNode) {
tmp = ((PackageNode) child).getPackageName();
} else if (child instanceof ClassNode) {
tmp = ((ClassNode) child).getClassName();
} else {
return false;
}
if (tmp != null && tmp.equals(str)) {
// goto level
level = child;
return true;
}
}
return false;
}
}