ReportTree.java
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 ((ViolationNodethis.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 = ((PackageNodechild).getPackageName();
179       else if (child instanceof ClassNode) {
180     tmp = ((ClassNodechild).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 }