001: package net.sourceforge.pmd.dfa.report;
002:
003: import net.sourceforge.pmd.IRuleViolation;
004:
005: import java.util.Iterator;
006:
007: public class ReportTree {
008:
009: private PackageNode rootNode = new PackageNode("");
010: private AbstractReportNode level;
011:
012: private class TreeIterator implements Iterator<IRuleViolation> {
013:
014: private AbstractReportNode iterNode = rootNode;
015: private boolean hasNextFlag;
016:
017: public void remove() {
018: throw new UnsupportedOperationException();
019: }
020:
021: public boolean hasNext() {
022: this .hasNextFlag = true;
023: return this .getNext() != null;
024: }
025:
026: public IRuleViolation next() {
027:
028: if (!this .hasNextFlag) {
029: this .getNext();
030: } else {
031: this .hasNextFlag = false;
032: }
033:
034: if (this .iterNode instanceof ViolationNode) {
035: return ((ViolationNode) this .iterNode)
036: .getRuleViolation();
037: }
038: return null;
039: }
040:
041: /**
042: * It's some kind of left-right-middle search (postorder).
043: * It always returns only
044: * leafs. The first node he returns is the most left handed leaf he can
045: * found. Now he's looking for siblings and if there are any, he starts
046: * searching for the next most left handed leaf. If there are no
047: * siblings he goes up to his parent and starts looking for siblings.
048: * If there are any he starts searching for the next most left handed
049: * leaf again. And so on ... until he wants to get the parent of the
050: * root node. Because there is no one, the search stops.
051: */
052:
053: private AbstractReportNode getNext() {
054: AbstractReportNode node;
055:
056: while (true) {
057: if (this .iterNode.isLeaf()) {
058:
059: while ((node = (this .iterNode).getNextSibling()) == null) {
060:
061: node = this .iterNode.getParent();
062: if (node == null) {
063: return null;
064: } else {
065: this .iterNode = node;
066: }
067: }
068:
069: this .iterNode = node;
070: if (this .iterNode.isLeaf()) {
071: return this .iterNode;
072: } else {
073: continue;
074: }
075: } else {
076: this .iterNode = this .iterNode.getFirstChild();
077: if (this .iterNode.isLeaf()) {
078: return this .iterNode;
079: } else {
080: continue;
081: }
082: }
083: }
084: }
085: }
086:
087: public Iterator<IRuleViolation> iterator() {
088: return new TreeIterator();
089: }
090:
091: public int size() {
092: int count = 0;
093: for (Iterator<IRuleViolation> i = iterator(); i.hasNext();) {
094: i.next();
095: count++;
096: }
097: return count;
098: }
099:
100: public AbstractReportNode getRootNode() {
101: return rootNode;
102: }
103:
104: /**
105: * Adds the RuleViolation to the tree. Splits the package name. Each
106: * package, class and violation gets there own tree node.
107: */
108: public void addRuleViolation(IRuleViolation violation) {
109: String pack = violation.getPackageName();
110: String[] a = {};
111: if (pack == null) {
112: a = new String[] { "" };
113: } else if (pack.indexOf('.') != -1) {
114: String[] tmp = pack.split("\\.");
115: a = new String[tmp.length];
116: System.arraycopy(tmp, 0, a, 0, tmp.length);
117: } else {
118: a = new String[] { pack };
119: }
120:
121: this .level = this .rootNode;
122: String plugedPackageName = "";
123:
124: for (int i = 0; i < a.length; i++) {
125: String packageName = a[i];
126: plugedPackageName += packageName + '.';
127:
128: if (!this .isStringInLevel(plugedPackageName)) {
129: PackageNode node = new PackageNode(plugedPackageName);
130: this .level.addFirst(node);
131: // gotoLevel
132: this .level = node;
133: }
134: }
135:
136: String cl = violation.getClassName();
137:
138: if (!this .isStringInLevel(cl)) {
139: ClassNode node = new ClassNode(cl);
140: this .level.addFirst(node);
141: // gotoLevel
142: this .level = node;
143: }
144:
145: /*
146: * Filters dublicated rule violations. Like the comparator in
147: * RuleViolation if he already exists.
148: */
149: ViolationNode tmp = new ViolationNode(violation);
150: if (!this .equalsNodeInLevel(this .level, tmp)) {
151: this .level.add(tmp);
152: }
153: }
154:
155: /**
156: * Checks if node is a child of the level node.
157: */
158: private boolean equalsNodeInLevel(AbstractReportNode level,
159: AbstractReportNode node) {
160: for (int i = 0; i < level.getChildCount(); i++) {
161: if ((level.getChildAt(i)).equalsNode(node)) {
162: return true;
163: }
164: }
165: return false;
166: }
167:
168: /**
169: * Checks if the packageName or the className is a child of the current
170: * (this.level) node. If it's true, the current node changes to the
171: * child node.
172: */
173: private boolean isStringInLevel(String str) {
174:
175: for (int i = 0; i < this .level.getChildCount(); i++) {
176: AbstractReportNode child = this .level.getChildAt(i);
177: String tmp = null;
178:
179: if (child instanceof PackageNode) {
180: tmp = ((PackageNode) child).getPackageName();
181: }
182: if (child instanceof ClassNode) {
183: tmp = ((ClassNode) child).getClassName();
184: }
185:
186: if (tmp == null) {
187: return false;
188: }
189:
190: if (tmp.equals(str)) {
191: // goto level
192: this .level = child;
193: return true;
194: }
195: }
196: return false;
197: }
198:
199: }
|