Project Report: fawkez

Packagesummary org.jcoderz.phoenix.report

org.jcoderz.phoenix.report.FileSummary

LineHitsNoteSource
1  /*
2   * $Id: FileSummary.java 1336 2009-03-28 22:04:07Z amandel $
3   *
4   * Copyright 2006, The jCoderZ.org Project. All rights reserved.
5   *
6   * Redistribution and use in source and binary forms, with or without
7   * modification, are permitted provided that the following conditions are
8   * met:
9   *
10   *    * Redistributions of source code must retain the above copyright
11   *      notice, this list of conditions and the following disclaimer.
12   *    * Redistributions in binary form must reproduce the above
13   *      copyright notice, this list of conditions and the following
14   *      disclaimer in the documentation and/or other materials
15   *      provided with the distribution.
16   *    * Neither the name of the jCoderZ.org Project nor the names of
17   *      its contributors may be used to endorse or promote products
18   *      derived from this software without specific prior written
19   *      permission.
20   *
21   * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND
22   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24   * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS
25   * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
28   * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29   * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30   * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31   * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32   */
33  package org.jcoderz.phoenix.report;
34  
35  import java.io.Serializable;
36  import java.text.DecimalFormat;
37  import java.text.NumberFormat;
38  import java.util.Comparator;
39  import java.util.Iterator;
40  
41  import org.jcoderz.commons.util.Assert;
42  import org.jcoderz.commons.util.StringUtil;
43  import org.jcoderz.phoenix.report.jaxb.File;
44  import org.jcoderz.phoenix.report.jaxb.Item;
45  
46  /**
47   * This class encapsulates all finding information collected
48   * for a file or a group of files.
49   *
50   * <p>This class also allows to perform the magic quality
51   * calculation for the data collected in the summary.</p>
52   *
53   * @author Andreas Mandel
54   */
55  public final class FileSummary
56          implements Comparable<FileSummary>
57  {
58      /** Constant used for initial string buffer size. */
59      private static final int STRING_BUFFER_SIZE = 256;
60  
61      /** Constant for percentage calculation 1% = 1 / MAX_PERCENTAGE. */
62      private static final int MAX_PERCENTAGE = 100;
63  
64      private static final float MAX_PERCENTAGE_FLOAT = 100;
65  
66100     private final NumberFormat mCoveragePercantageFormatter =
67          new DecimalFormat("##0.00");
68  
69      /** Counts the number of files added up in this summary. */
70100     private int mFiles = 0;
71  
72      /** Lines of code in the file. */
73      private int mLinesOfCode;
74  
75      /**
76       * Lines of code in the file that contain coverage information
77       * that is not 0.
78       */
79      private int mCoveredLinesOfCode;
80  
81      /**
82       * Holds the number of violations for each severity level.
83       */
84100     private int[] mViolations = new int[Severity.VALUES.size()];
85  
86      /**
87       * Percentage values for the violations.
88       * Data stored in here is only valid if <code>mPercentUpToDate</code>
89       * is true.
90       */
91100     private int[] mPercent = new int[Severity.VALUES.size()];
92100     private boolean mPercentUpToDate = false;
93  
94      private final String mClassName;
95      private final String mPackage;
96      private final String mDetailedFile;
97      private boolean mCoverageData;
98  
99      /**
100       * Creates a new empty file summary object used to summarize
101       * findings for classes in all packages.
102       */
103      public FileSummary ()
104      {
105100         this ("Global Summary", "all", null, 0, false);
106100     }
107  
108      /**
109       * Creates a new empty file summary object used to summarize
110       * findings for classes in in the given package.
111       *
112       * @param packagename name of the package where this summary
113       *      is used for.
114       */
115      public FileSummary (String packagename)
116      {
1170         this ("Package Summary", packagename, null, 0, false);
1180     }
119  
120      /**
121       * Creates a new empty file summary object used to summarize
122       * findings for the given class in the given package with
123       * link to the file and code information.
124       * @param className the name of the class (without package
125       *      information).
126       * @param packagename the name of the package where the class
127       *      resides in.
128       * @param reportfile the name of the file where the html report
129       *      stored.
130       * @param linesOfCode the number of lines in the file.
131       * @param withCoverage true if coverage information is
132       *      available.
133       */
134      public FileSummary (String className, String packagename,
135          String reportfile, int linesOfCode, boolean withCoverage)
136100     {
137100         mClassName = className;
138100         mPackage = packagename;
139100         mDetailedFile = reportfile;
140100         mLinesOfCode = linesOfCode;
141100         mCoverageData = withCoverage;
142100     }
143  
144      /**
145       * Calculates the quality as percentage represented as float.
146       * @param loc total number of lines of code. This is also the maximum
147       *         that might be returned by this method.
148       * @param violations the array holding the violations of the severity
149       *      related to the position in the array. The elements of the
150       *      array are NOT modified.
151       * @return the quality as percentage represented as float.
152       */
153      public static float calculateQuality (int loc, int[] violations)
154      {
1550         float quality = 0;
1560         if (loc > 0)
157          {
1580             quality = calcUnweightedQuality(loc, violations);
1590             quality = (quality * MAX_PERCENTAGE) / loc;
160          }
1610         return quality;
162      }
163  
164      /**
165       * Calculates the unweighed quality points scored for the code.
166       * Maximum returned is <code>loc</code> the minimum is <code>0</code>.
167       * @param loc total number of lines of code. This is also the maximum
168       *         that might be returned by this method.
169       * @param violations the array holding the violations of the severity
170       *      related to the position in the array. The elements of the
171       *      array are NOT modified.
172       * @return the unweighed quality score.
173       */
174      private static int calcUnweightedQuality (int loc, int[] violations)
175      {
1760         Assert.assertEquals(
177              "Violations array length must match number of severities.",
178              Severity.VALUES.size(), violations.length);
1790         int quality = loc * Severity.PENALTY_SCALE; // lines of code
1800         for (int i = 0; i < Severity.VALUES.size() && quality > 0; i++)
181          {
182              // not covered lines are bad this
183              // not files with no coverage test at all get no penalty here!
1840             quality -= violations[i] * Severity.fromInt(i).getPenalty();
185          }
1860         if (quality < 0)
187          {
1880             quality = 0;
189          }
190          else
191          {
1920             quality /= Severity.PENALTY_SCALE;
193          }
1940         return quality;
195      }
196  
197      /**
198       * Calculates the quality percentage scored for the code.
199       * Maximum returned is <code>100</code> the minimum is <code>0</code>.
200       * @param loc total number of lines of code. This is also the maximum
201       *         that might be returned by this method.
202       * @param info number of info level findings.
203       * @param warning number of warning level findings.
204       * @param error number of error level findings.
205       * @param coverage number of coverage level findings.
206       * @param filtered number of filtered level findings.
207       * @param codestyle number of codestyle level findings.
208       * @param design number of design level findings.
209       * @param cpd number of cpd level findings.
210       * @return the unweighed quality score.
211       */
212      public static float calculateQuality (int loc, int info, int warning,
213          int error, int coverage, int filtered, int codestyle, int design,
214          int cpd)
215      {
2160         final int[] violations = new int[Severity.VALUES.size()];
2170         violations[Severity.INFO.toInt()] = info;
2180         violations[Severity.COVERAGE.toInt()] = coverage;
2190         violations[Severity.WARNING.toInt()] = warning;
2200         violations[Severity.ERROR.toInt()] = error;
2210         violations[Severity.FILTERED.toInt()] = filtered;
2220         violations[Severity.CODE_STYLE.toInt()] = codestyle;
2230         violations[Severity.DESIGN.toInt()] = design;
2240         violations[Severity.CPD.toInt()] = cpd;
2250         return FileSummary.calculateQuality(loc, violations);
226      }
227  
228  
229      /** @return the name of the class (without package information). */
230      public String getClassName ()
231      {
2320         return mClassName;
233      }
234  
235      /** @return the name of the package. */
236      public String getPackage ()
237      {
2380         return mPackage;
239      }
240  
241      /** @return the number of files summarized in this file summary. */
242      public int getNumberOfFiles ()
243      {
2440         return mFiles;
245      }
246  
247      /** {@inheritDoc} */
248      public String toString ()
249      {
2500         final StringBuilder result = new StringBuilder();
2510         calcPercent();
252  
2530         result.append(getFullClassName());
2540         result.append("{ LOC:");
2550         result.append(mLinesOfCode);
2560         result.append('(');
2570         result.append(mViolations[Severity.OK.toInt()]);
2580         result.append("%)");
2590(1)        final Iterator<Severity> i = Severity.VALUES.iterator();
2600         while (i.hasNext())
261          {
2620             final Severity s = i.next();
2630             if (mViolations[s.toInt()] != 0)
264              {
2650                 result.append(", ");
2660                 result.append(s.toString());
2670                 result.append(':');
2680                 result.append(mViolations[s.toInt()]);
2690                 result.append('(');
2700                 result.append(mPercent[s.toInt()]);
2710                 result.append("%)");
272              }
2730         }
2740         result.append('}');
2750         return result.toString();
276      }
277  
278      /**
279       * Add the counters of an other FileSummary to this one.
280       * @param other the FileSummary be added.
281       */
282      public void add (FileSummary other)
283      {
2840         for (int i = 0; i < mViolations.length; i++)
285          {
2860             mViolations[i] += other.mViolations[i];
287          }
2880         mLinesOfCode += other.mLinesOfCode;
2890         mCoveredLinesOfCode += other.mCoveredLinesOfCode;
2900         mPercentUpToDate = false;
2910         mFiles++;
2920         if (mCoverageData || other.isWithCoverage()
293              || mCoveredLinesOfCode > 0
294              || getNotCoveredLinesOfCode() > 0)
295          {
2960             mCoverageData = true;
297          }
2980     }
299  
300      /**
301       * Adds the counters from the given file to this summary.
302       * @param file the data to be added.
303       */
304      public void add (File file)
305      {
3060         mFiles++;
3070         mLinesOfCode += file.getLoc();
3080(2)        final Iterator<Item> i = file.getItem().iterator();
3090         while (i.hasNext())
310          {
3110             final Item item = i.next();
3120             final Severity severity = item.getSeverity();
3130             addViolation(severity);
3140         }
3150     }
316  
317      /**
318       * Returns true if this summary contains coverage data.
319       * @return true if this summary contains coverage data.
320       */
321      public boolean isWithCoverage ()
322      {
3230         return mCoverageData;
324      }
325  
326      /** Increments the counter of covered lines. */
327      public void addCoveredLine ()
328      {
3290         mPercentUpToDate = false;
3300         mCoveredLinesOfCode++;
3310     }
332  
333      /**
334       * Increments the counter for the given severity in this summary.
335       * @param severity the severity of the counter to be incremented.
336       */
337      public void addViolation (Severity severity)
338      {
339100         Assert.notNull(severity, "severity");
340100         mPercentUpToDate = false;
341100         mViolations[severity.toInt()]++;
342100     }
343  
344      /**
345       * @return the full class name including package declaration.
346       */
347      public String getFullClassName ()
348      {
349          final String fullClassName;
3500         if (StringUtil.isEmptyOrNull(mPackage))
351          {
3520             fullClassName = mClassName;
353          }
354          else
355          {
3560             fullClassName = mPackage + "." + mClassName;
357          }
3580         return fullClassName;
359      }
360  
361      /** @return the report file associated to this FileSummary. */
362      public String getHtmlLink ()
363      {
3640         return mDetailedFile;
365      }
366  
367      /** @return the number of lines of code. */
368      public int getLinesOfCode ()
369      {
3700         return mLinesOfCode;
371      }
372  
373      /**
374       * Returns the magic quality as percentage int.
375       * The maximum quality code gets a score of 100. The lowest score
376       * possible is 0.
377       * @return the magic quality as percentage int (0-100).
378       */
379      public int getQuality ()
380      {
3810         int quality = 0;
3820         if (mLinesOfCode > 0)
383          {
3840             quality = calcUnweightedQuality(mLinesOfCode, mViolations);
3850             quality = (quality * MAX_PERCENTAGE) / mLinesOfCode;
386          }
3870         return quality;
388      }
389  
390      /**
391       * Returns the magic quality as percentage float.
392       * The maximum quality code gets a score of 100. The lowest score
393       * possible is 0.
394       * @return the magic quality as percentage float (0.0-100.0).
395       */
396      public float getQualityAsFloat ()
397      {
398          // might be we should cache the result?
3990         return FileSummary.calculateQuality (mLinesOfCode, mViolations);
400      }
401  
402      /**
403       * Generates a string containing xhtml code that renders to a
404       * percentage bar that can be used as component of a web page.
405       * @return a string containing xhtml.
406       */
407      public String getPercentBar ()
408      {
4090         calcPercent();
4100         final StringBuilder sb = new StringBuilder(STRING_BUFFER_SIZE);
4110         sb.append("<table width='100%' cellspacing='0' cellpadding='0' "
412              + "summary='quality-bar'><tr valign='middle'>");
4130         for (int i = Severity.OK.toInt(); i < Severity.MAX_SEVERITY_INT; i++)
414          {
4150             if (mPercent[i] > 0)
416              {
4170                 sb.append("<td class='");
4180                 sb.append(Severity.fromInt(i).toString());
4190                 sb.append("' width='");
4200                 sb.append(mPercent[i]);
4210                 sb.append("%' height='10'></td>");
422              }
423          }
4240         sb.append("</tr></table>");
4250         return sb.toString();
426      }
427  
428      /**
429       * Generates a string containing xhtml code that renders to a
430       * bar that can be used as component of a web page to represent
431       * the amount of covered code.
432       * @return a string containing xhtml.
433       */
434      public String getCoverageBar ()
435      {
4360         final int notCovered = MAX_PERCENTAGE - getCoverage();
437  
4380         final StringBuilder sb = new StringBuilder(STRING_BUFFER_SIZE);
4390         sb.append("<table width='100%' cellspacing='0' cellpadding='0' "
440              + "summary='coverage-bar'><tr valign='middle'>");
4410         if (notCovered < MAX_PERCENTAGE)
442          {
4430             sb.append("<td class='ok' width='");
4440             sb.append(MAX_PERCENTAGE - notCovered);
4450             sb.append("%' height='10'></td>");
446          }
4470         if (notCovered != 0)
448          {
4490             sb.append("<td class='error' width='");
4500             sb.append(notCovered);
4510             sb.append("%' height='10'></td></tr>");
452          }
4530         sb.append("</tr></table>");
4540         return sb.toString();
455      }
456  
457      /**
458       * Returns the number of violations for the given severity.
459       * @param severity the severity to check.
460       * @return the number of violations for the given severity
461       */
462      public int getViolations (Severity severity)
463      {
4640         return mViolations[severity.toInt()];
465      }
466  
467      /**
468       * Get the coverage percentage in double precision.
469       * @return the coverage percentage in double precision.
470       */
471      public float getCoverageAsFloat ()
472      {
4730         final float allLinesOfCode
474              = getNotCoveredLinesOfCode() + mCoveredLinesOfCode;
475          float result;
4760         if (allLinesOfCode != 0)
477          {
4780             result = mCoveredLinesOfCode / allLinesOfCode;
479          }
4800         else if (getNotCoveredLinesOfCode() > 0)
481          {
4820             result = 0;
483          }
484          else // no coverage at all (might be interface...
485          {
4860             result = 1;
487          }
4880         return result * MAX_PERCENTAGE_FLOAT;
489      }
490  
491      /**
492       * Returns the coverage as user string.
493       * @return the coverage as user string.
494       */
495      public String getCoverageAsString ()
496      {
4970         return mCoveragePercantageFormatter.format(getCoverageAsFloat()) + "%";
498      }
499  
500      /** @return the coverage percentage as int. */
501      public int getCoverage ()
502      {
5030         final int notCoveredLinesOfCode = getNotCoveredLinesOfCode();
504  
505          int notCovered;
5060         if (mCoveredLinesOfCode != 0)
507          {
5080             notCovered = (notCoveredLinesOfCode * MAX_PERCENTAGE)
509                      / (mCoveredLinesOfCode + notCoveredLinesOfCode);
5100             if ((notCovered == 0) && (notCoveredLinesOfCode > 0))
511              { // below 1% -> round up to 1%
5120                 notCovered = 1;
513              }
514          }
5150         else if (notCoveredLinesOfCode > 0)
516          {
5170             notCovered = MAX_PERCENTAGE;
518          }
519          else // no coverage at all (might be interface...
520          {
5210             notCovered = 0;
522          }
5230         return MAX_PERCENTAGE - notCovered;
524      }
525  
526      /**
527       * @return the number of not covered lines of code.
528       */
529      public int getNotCoveredLinesOfCode ()
530      {
5310         return mViolations[Severity.COVERAGE.toInt()];
532      }
533  
534      /**
535       * All findings that are between {@link Severity#INFO} and
536       * {@link Severity#ERROR} but not {@link Severity#COVERAGE}
537       * are counted.
538       * @return the number of violations summed up in this summary.
539       */
540      public int getNumberOfFindings ()
541      {
542100         int sum = 0;
543100         for (int i = Severity.INFO.toInt(); i <= Severity.ERROR.toInt(); i++)
544          {
545100             if (i != Severity.COVERAGE.toInt())
546              {
547100                 sum += mViolations[i];
548              }
549          }
550100         return sum;
551      }
552  
553      /** {@inheritDoc} */
554      public int compareTo (FileSummary o)
555      {
5560(3)        int result = 0;
5570         if (mPackage != null)
558          {
5590             result = mPackage.compareTo((o).mPackage);
560          }
5610         if (result == 0)
562          {
5630             if (getClassName() != null)
564              {
5650                 result = getClassName().compareTo(o.getClassName());
566              }
567          }
5680         return result;
569      }
570  
571  
572      private void calcPercent ()
573      {
5740         if (!mPercentUpToDate)
575          {
5760             doCalcPercent();
577          }
5780     }
579  
580      private void doCalcPercent ()
581      {
5820         int remainingPercentage = MAX_PERCENTAGE;
583          // errors
5840         if (mLinesOfCode != 0)
585          {
5860             for (int i = Severity.ERROR.toInt(); i > Severity.INFO.toInt();
5870                     i--)
588              {
589                  int percent;
590  
5910                 if (i == Severity.COVERAGE.toInt())
592                  {
5930                     percent = calcPercentCoverage();
594                  }
595                  else
596                  {
5970                     percent = calcPercentage(
598                          mViolations[i] * Severity.fromInt(i).getPenalty(),
599                          mLinesOfCode * Severity.PENALTY_SCALE);
600                  }
601                  // do not round to 0.
6020                 if (mViolations[i] > 0 && percent == 0)
603                  {
6040                     percent = 1;
605                  }
6060                 if (percent > remainingPercentage)
607                  {
6080                     percent = remainingPercentage;
609                  }
6100                 mPercent[i] = percent;
6110                 remainingPercentage -= percent;
612              }
613          }
614          else
615          {
6160             for (int i = Severity.ERROR.toInt(); i > Severity.INFO.toInt(); i--)
617              {
6180                 mPercent[i] = 0;
619              }
620          }
6210         mPercent[Severity.OK.toInt()] = remainingPercentage;
6220         mPercentUpToDate = true;
6230     }
624  
625      /**
626       * Calculates the penalty percentage of the coverage tests.
627       */
628      private int calcPercentCoverage ()
629      {
630          final int coverageViolationPercentage;
6310         if (!mCoverageData)
632          {
6330             coverageViolationPercentage = 0;
634          }
635          else
636          {
6370             final int notCoveredLines
638                  = getNotCoveredLinesOfCode();
6390             coverageViolationPercentage = calcPercentage(
640                  notCoveredLines * Severity.COVERAGE.getPenalty(),
641                  Severity.PENALTY_SCALE
642                  * (mCoveredLinesOfCode + notCoveredLines));
643          }
6440         return coverageViolationPercentage;
645      }
646  
647      private static int calcPercentage (int part, int all)
648      {
649          final int result;
6500         if (all == 0)
651          {
6520             result = 0;
653          }
654          else
655          {
6560             result = part * MAX_PERCENTAGE / all;
657          }
6580         return result;
659      }
660  
661  
662      /**
663       * Comparator that allows to sort the FileSummary by name of the package.
664       * @author Andreas Mandel
665       */
6660     static final class SortByPackage
667          implements Comparator<FileSummary>, Serializable
668      {
669          private static final long serialVersionUID = 2244367340241672131L;
670  
671          public int compare (FileSummary o1, FileSummary o2)
672          {
6730             return o1.compareTo(o2);
674          }
675      }
676  
677      /**
678       * Comparator that allows to sort the FileSummary by quality.
679       * @author Andreas Mandel
680       */
6810     static final class SortByQuality
682          implements Comparator<FileSummary>, Serializable
683      {
684          private static final long serialVersionUID = 1718175789352629538L;
685  
686          public int compare (FileSummary o1, FileSummary o2)
687          {
688              int result;
6890             final float qualityA = o1.getQualityAsFloat();
6900             final float qualityB = o2.getQualityAsFloat();
6910             if (qualityA < qualityB)
692              {
6930                 result = -1;
694              }
6950             else if (qualityA > qualityB)
696              {
6970                 result = 1;
698              }
699              else
700              {
7010                 result = o1.compareTo(o2);
702              }
7030             return result;
704          }
705      }
706  
707      /**
708       * Comparator that allows to sort the FileSummary by coverage.
709       * @author Andreas Mandel
710       */
7110     static final class SortByCoverage
712          implements Comparator<FileSummary>, Serializable
713      {
714          private static final long serialVersionUID = -4275903074787742250L;
715  
716          public int compare (FileSummary a, FileSummary b)
717          {
7180             final float coverA = a.getCoverageAsFloat();
7190             final float coverB = b.getCoverageAsFloat();
720  
721              final int result;
7220             if (coverA < coverB)
723              {
7240                 result = -1;
725              }
7260             else if (coverA > coverB)
727              {
7280                 result = 1;
729              }
7300(4)            else if (a.getNotCoveredLinesOfCode() > b.getNotCoveredLinesOfCode())
731              {
7320                 result = -1;
733              }
7340(5)            else if (a.getNotCoveredLinesOfCode() < b.getNotCoveredLinesOfCode())
735              {
7360                 result = 1;
737              }
738              else
739              {
7400                 result = a.compareTo(b);
741              }
7420             return result;
743          }
744      }
745  }

Findings in this File

d (1) 259 : 62 [unchecked] unchecked conversion found : java.util.Iterator required: java.util.Iterator<org.jcoderz.phoenix.report.Severity>
d (2) 308 : 57 [unchecked] unchecked conversion found : java.util.Iterator required: java.util.Iterator<org.jcoderz.phoenix.report.jaxb.Item>
w (3) 556 : 0 org.jcoderz.phoenix.report.FileSummary defines compareTo(FileSummary) and uses Object.equals()
c (4) 730 : 0 Line is longer than 80 characters.
c (5) 734 : 0 Line is longer than 80 characters.