001 package net.sourceforge.pmd.lang.dfa.report;
002
003 import java.util.Iterator;
004
005 import net.sourceforge.pmd.RuleViolation;
006
007 public class ReportTree {
008
009 private PackageNode rootNode = new PackageNode("");
010 private AbstractReportNode level;
011
012 private class TreeIterator implements Iterator<RuleViolation> {
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 RuleViolation next() {
027 if (!this.hasNextFlag) {
028 this.getNext();
029 } else {
030 this.hasNextFlag = false;
031 }
032
033 if (this.iterNode instanceof ViolationNode) {
034 return ((ViolationNode) this.iterNode).getRuleViolation();
035 }
036 return null;
037 }
038
039 /**
040 * It's some kind of left-right-middle search (postorder).
041 * It always returns only
042 * leafs. The first node he returns is the most left handed leaf he can
043 * found. Now he's looking for siblings and if there are any, he starts
044 * searching for the next most left handed leaf. If there are no
045 * siblings he goes up to his parent and starts looking for siblings.
046 * If there are any he starts searching for the next most left handed
047 * leaf again. And so on ... until he wants to get the parent of the
048 * root node. Because there is no one, the search stops.
049 */
050
051 private AbstractReportNode getNext() {
052 AbstractReportNode node;
053
054 while (true) {
055 if (this.iterNode.isLeaf()) {
056
057 while ((node = this.iterNode.getNextSibling()) == null) {
058
059 node = this.iterNode.getParent();
060 if (node == null) {
061 return null;
062 } else {
063 this.iterNode = node;
064 }
065 }
066
067 this.iterNode = node;
068 if (this.iterNode.isLeaf()) {
069 return this.iterNode;
070 } else {
071 continue;
072 }
073 } else {
074 this.iterNode = this.iterNode.getFirstChild();
075 if (this.iterNode.isLeaf()) {
076 return this.iterNode;
077 } else {
078 continue;
079 }
080 }
081 }
082 }
083 }
084
085 public Iterator<RuleViolation> iterator() {
086 return new TreeIterator();
087 }
088
089 public int size() {
090 int count = 0;
091 for (Iterator<RuleViolation> i = iterator(); i.hasNext();) {
092 i.next();
093 count++;
094 }
095 return count;
096 }
097
098 public AbstractReportNode getRootNode() {
099 return rootNode;
100 }
101
102 /**
103 * Adds the RuleViolation to the tree. Splits the package name. Each
104 * package, class and violation gets there own tree node.
105 */
106 public void addRuleViolation(RuleViolation violation) {
107 String packageName = violation.getPackageName();
108 if (packageName == null) {
109 packageName = "";
110 }
111
112 this.level = this.rootNode;
113
114 int endIndex = packageName.indexOf('.');
115 while (true) {
116 String parentPackage;
117 if (endIndex < 0) {
118 parentPackage = packageName;
119 } else {
120 parentPackage = packageName.substring(0, endIndex);
121 }
122
123 if (!this.isStringInLevel(parentPackage)) {
124 PackageNode node = new PackageNode(parentPackage);
125 this.level.addFirst(node);
126 // gotoLevel
127 this.level = node;
128 }
129
130 if (endIndex < 0) {
131 break;
132 }
133 endIndex = packageName.indexOf('.', endIndex + 1);
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 duplicated 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, AbstractReportNode node) {
159 for (int i = 0; i < level.getChildCount(); i++) {
160 if (level.getChildAt(i).equalsNode(node)) {
161 return true;
162 }
163 }
164 return false;
165 }
166
167 /**
168 * Checks if the packageName or the className is a child of the current
169 * (this.level) node. If it's true, the current node changes to the
170 * child node.
171 */
172 private boolean isStringInLevel(String str) {
173
174 for (int i = 0; i < this.level.getChildCount(); i++) {
175 final AbstractReportNode child = this.level.getChildAt(i);
176 final String tmp;
177 if (child instanceof PackageNode) {
178 tmp = ((PackageNode) child).getPackageName();
179 } else if (child instanceof ClassNode) {
180 tmp = ((ClassNode) child).getClassName();
181 } else {
182 return false;
183 }
184
185 if (tmp != null && tmp.equals(str)) {
186 // goto level
187 this.level = child;
188 return true;
189 }
190 }
191 return false;
192 }
193
194 }
|