View Javadoc

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