View Javadoc

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             // TODO Remove when minimal runtime support is >= JDK 1.4
120             try {
121                 Method split = String.class.getMethod("split", new Class[]{String.class});
122                 if (split != null) {
123                     //        // Compatible with >= JDK 1.4
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                 // Compatible with < JDK 1.4
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                 // gotoLevel
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             // gotoLevel
173             this.level = node;
174         }
175 
176         /*
177          * Filters dublicated rule violations. Like the comparator in
178          * RuleViolation if he already exists.
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                 // goto level
222                 this.level = child;
223                 return true;
224             }
225         }
226         return false;
227     }
228 
229 }