1 package net.sourceforge.pmd.dfa.report;
2
3 import net.sourceforge.pmd.IRuleViolation;
4
5 import java.lang.reflect.InvocationTargetException;
6 import java.lang.reflect.Method;
7 import java.util.ArrayList;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.StringTokenizer;
11
12 public class ReportTree {
13
14 private PackageNode rootNode = new PackageNode("");
15 private AbstractReportNode level;
16
17 private class TreeIterator implements Iterator {
18
19 private AbstractReportNode iterNode = rootNode;
20 private boolean hasNextFlag;
21
22 public void remove() {
23 throw new UnsupportedOperationException();
24 }
25
26 public boolean hasNext() {
27 this.hasNextFlag = true;
28 return this.getNext() != null;
29 }
30
31 public Object next() {
32
33 if (!this.hasNextFlag) {
34 this.getNext();
35 } else {
36 this.hasNextFlag = false;
37 }
38
39 if (this.iterNode instanceof ViolationNode) {
40 return ((ViolationNode) this.iterNode).getRuleViolation();
41 }
42 return null;
43 }
44
45 /***
46 * It's some kind of left-right-middle search (postorder).
47 * It always returns only
48 * leafs. The first node he returns is the most left handed leaf he can
49 * found. Now he's looking for siblings and if there are any, he starts
50 * searching for the next most left handed leaf. If there are no
51 * siblings he goes up to his parent and starts looking for siblings.
52 * If there are any he starts searching for the next most left handed
53 * leaf again. And so on ... until he wants to get the parent of the
54 * root node. Because there is no one, the search stops.
55 */
56
57 private Object getNext() {
58 AbstractReportNode node;
59
60 while (true) {
61 if (this.iterNode.isLeaf()) {
62
63 while ((node = (this.iterNode).getNextSibling()) == null) {
64
65 node = this.iterNode.getParent();
66 if (node == null) {
67 return null;
68 } else {
69 this.iterNode = node;
70 }
71 }
72
73 this.iterNode = node;
74 if (this.iterNode.isLeaf()) {
75 return this.iterNode;
76 } else {
77 continue;
78 }
79 } else {
80 this.iterNode = this.iterNode.getFirstChild();
81 if (this.iterNode.isLeaf()) {
82 return this.iterNode;
83 } else {
84 continue;
85 }
86 }
87 }
88 }
89 }
90
91
92 public Iterator iterator() {
93 return new TreeIterator();
94 }
95
96 public int size() {
97 int count = 0;
98 for (Iterator i = iterator(); i.hasNext();) {
99 i.next();
100 count++;
101 }
102 return count;
103 }
104
105 public AbstractReportNode getRootNode() {
106 return rootNode;
107 }
108
109 /***
110 * Adds the RuleViolation to the tree. Splits the package name. Each
111 * package, class and violation gets there own tree node.
112 */
113 public void addRuleViolation(IRuleViolation violation) {
114 String pack = violation.getPackageName();
115 String[] a = {};
116 if (pack == null) {
117 a = new String[]{""};
118 } else if (pack.indexOf('.') != -1) {
119
120 try {
121 Method split = String.class.getMethod("split", new Class[]{String.class});
122 if (split != null) {
123
124 Object[] tmp = (Object[]) split.invoke(pack, new Object[]{"//."});
125 a = new String[tmp.length];
126 for (int i = 0; i < tmp.length; i++) {
127 a[i] = (String) tmp[i];
128 }
129 }
130 } catch (IllegalAccessException e) {
131 e.printStackTrace();
132 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
133 } catch (IllegalArgumentException e) {
134 e.printStackTrace();
135 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
136 } catch (InvocationTargetException e) {
137 e.printStackTrace();
138 throw new InternalError("Runtime reports to be >= JDK 1.4 yet String.split(java.lang.String) is broken.");
139 } catch (NoSuchMethodException nsme) {
140
141 StringTokenizer toker = new StringTokenizer(pack, ".");
142 List parts = new ArrayList();
143 while (toker.hasMoreTokens()) {
144 parts.add(toker.nextToken());
145 }
146 a = (String[]) parts.toArray(new String[parts.size()]);
147 }
148 } else {
149 a = new String[]{pack};
150 }
151
152 this.level = this.rootNode;
153 String plugedPackageName = "";
154
155 for (int i = 0; i < a.length; i++) {
156 String packageName = a[i];
157 plugedPackageName += packageName + '.';
158
159 if (!this.isStringInLevel(plugedPackageName)) {
160 PackageNode node = new PackageNode(plugedPackageName);
161 this.level.addFirst(node);
162
163 this.level = node;
164 }
165 }
166
167 String cl = violation.getClassName();
168
169 if (!this.isStringInLevel(cl)) {
170 ClassNode node = new ClassNode(cl);
171 this.level.addFirst(node);
172
173 this.level = node;
174 }
175
176
177
178
179
180 ViolationNode tmp = new ViolationNode(violation);
181 if (!this.equalsNodeInLevel(this.level, tmp)) {
182 this.level.add(tmp);
183 }
184 }
185
186 /***
187 * Checks if node is a child of the level node.
188 */
189 private boolean equalsNodeInLevel(AbstractReportNode level, AbstractReportNode node) {
190 for (int i = 0; i < level.getChildCount(); i++) {
191 if ((level.getChildAt(i)).equalsNode(node)) {
192 return true;
193 }
194 }
195 return false;
196 }
197
198 /***
199 * Checks if the packageName or the className is a child of the current
200 * (this.level) node. If it's true, the current node changes to the
201 * child node.
202 */
203 private boolean isStringInLevel(String str) {
204
205 for (int i = 0; i < this.level.getChildCount(); i++) {
206 AbstractReportNode child = this.level.getChildAt(i);
207 String tmp = null;
208
209 if (child instanceof PackageNode) {
210 tmp = ((PackageNode) child).getPackageName();
211 }
212 if (child instanceof ClassNode) {
213 tmp = ((ClassNode) child).getClassName();
214 }
215
216 if (tmp == null) {
217 return false;
218 }
219
220 if (tmp.equals(str)) {
221
222 this.level = child;
223 return true;
224 }
225 }
226 return false;
227 }
228
229 }