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 }