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 }