/** * 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; } }