Project Report: fawkez

Packagesummary org.jcoderz.phoenix.report

org.jcoderz.phoenix.report.FindingsSummary

LineHitsNoteSource
1  /*
2   * $Id: FindingsSummary.java 1454 2009-05-10 11:06:43Z 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.IOException;
36  import java.io.Writer;
37  import java.util.ArrayList;
38  import java.util.Arrays;
39  import java.util.Collection;
40  import java.util.Collections;
41  import java.util.HashMap;
42  import java.util.Iterator;
43  import java.util.List;
44  import java.util.Map;
45  
46  import org.jcoderz.commons.util.XmlUtil;
47  import org.jcoderz.phoenix.report.jaxb.Item;
48  
49  /**
50   * This class holds all findings, by type and file for the project.
51   *
52   * This class in NOT thread save in any way.
53   *
54   * @author Andreas Mandel
55   */
560 final class FindingsSummary
57  {
58     /** Singleton type findings collector. */
590    private static FindingsSummary sFindingsSummary = new FindingsSummary();
60  
610    private final Map<String, FindingSummary> mFindings
62         = new HashMap<String, FindingSummary>();
630    private int mOverallCounter = 0;
64  
65     private FindingsSummary ()
660    {
67        // singleton class only instantiated by the factory
680    }
69  
70     /**
71      * Utility method to get the Singleton.
72      * @return the one and only findings summary object.
73      */
74     public static FindingsSummary getFindingsSummary ()
75     {
760       return sFindingsSummary;
77     }
78  
79     /**
80      * Generates a key unique for kind of the given finding.
81      * @param finding item where to generate the key for.
82      * @return a key unique for kind of the given finding.
83      */
84      public static String getKeyForFinding (Item finding)
85      {
860         return finding.getFindingType() + "_"
87                    + finding.getSeverity().toString();
88      }
89  
90      /**
91       * Generates a key unique for kind of the given finding type and
92       *          severity.
93       * @param findingType the type to generate the key for.
94       * @param severity the severity to generate the key for.
95       * @return a key unique for kind of the given finding type and
96       *          severity.
97       */
98       public static String getKeyForFinding (FindingType findingType,
99               Severity severity)
100       {
1010          return findingType.getSymbol() + "_" + severity.toString();
102       }
103  
104     /**
105      * Adds the finding to the findings data structure.
106      * All references and counters are updated.
107      * @param finding the concrete item that was detected
108      * @param file the FileSummary object of the detected finding.
109      */
110     public static void addFinding (Item finding, FileSummary file)
111     {
1120       getFindingsSummary().getFindingSummary(finding)
113           .addFinding(finding, file);
1140    }
115  
116 (1)   /**
117      * Provides access to all findings of the given type.
118      * @param findingType the type of the finding.
119      * @param severity the severity of the finding.
120      * @return a FindingSummary of all findings of
121      *          the given FindingType, might be null if
122      *          no such finding exists.
123      */
124     public FindingSummary getFindingSummary (FindingType findingType,
125           Severity severity)
126     {
1270        return mFindings.get(getKeyForFinding(findingType, severity));
128     }
129  
130     /**
131      * Returns the FindingSummary appropriate to hold findings of the
132      * type of the given Item.
133      * If no such summary exists yet, a new one is generated and
134      * returned.
135      * @param item the item where to return a summary for.
136      * @return the FindingSummary appropriate to hold findings of the
137      *     type of the given Item.
138      */
139     public FindingSummary getFindingSummary (Item item)
140     {
1410       final String key = getKeyForFinding(item);
142        // cast to make sure we get an exception once item.getFindingType
143        // returns a real FindingType
1440       FindingSummary result = mFindings.get(key);
1450       if (result == null)
146        {
1470          result = new FindingSummary(item);
148        }
1490       return result;
150     }
151  
152     /**
153      * Returns the map mapping from the type/severity string to stored
154      * FindingSummary objects.
155      * The returned map is immutable. Stored objects MUST not be
156      * modified.
157      * @return the map mapping from the type/severity string to stored
158      * FindingSummary objects.
159      */
160     Map<String, FindingSummary> getFindings ()
161     {
1620       return Collections.unmodifiableMap(mFindings);
163     }
164  
165     /** {@inheritDoc} */
166     public String toString ()
167     {
1680       return "[FindingsSummary: " + mFindings + "(" + mOverallCounter + ")]";
169     }
170  
171      /**
172       * Generates a page that lists all findings, that links to the
173       * detailed finding pages. The content is ordered by severity and
174       * number of occurrences.
175       * @param out the writer where to write the html data to.
176       * @throws IOException if the data can not be written.
177       */
178      static void createOverallContent (Writer out) throws IOException
179      {
1800         final Collection<FindingSummary> colAllFindings
181                  = getFindingsSummary().getFindings().values();
1820         final FindingSummary[] allFindings
183                  = colAllFindings.toArray(
184                      new FindingSummary[colAllFindings.size()]);
185  
1860          Arrays.sort(allFindings);
187  
1880          Severity currentSeverity = null;
189  
1900          out.write("<table border='0' cellpadding='0' cellspacing='0' "
191                   + "width='95%' summary='Summary of all findings.'>");
1920          int row = 0;
1930          for (final FindingSummary summary : allFindings)
194           {
1950             if (summary.getSeverity() != currentSeverity)
196              {
1970                out.write("<tr><td colspan='3' class='severityheader'>");
1980                currentSeverity = summary.getSeverity();
1990                out.write("<a name='" + currentSeverity.toString() + "'/>");
2000                out.write("Severity: ");
2010                out.write(currentSeverity.toString());
2020                out.write("\n</td></tr>");
2030                row = 0;
204              }
2050             row++;
2060             out.write("<tr class='" + currentSeverity
207                    + Java2Html.toOddEvenString(row) + "'>");
2080             out.write("<td class='finding-counter'>");
2090             out.write(String.valueOf(summary.getCounter()));
2100             out.write("</td>");
2110             out.write("<td class='finding-origin'>");
2120             out.write(summary.getOrigin().toString());
2130             out.write("</td>");
2140             out.write("<td class='finding-data' width='100%'>");
215  
2160             out.write("<a href='");
2170             out.write(summary.createFindingDetailFilename());
2180             out.write("' title='");
2190             out.write(summary.getFindingType().getSymbol());
2200             out.write("'>");
221     // if (summary.isFindingsHaveSameMessage()
222     // && summary.getFindingMessage() != null)
223     // {
224     // out.write(summary.getFindingMessage());
225     // }
226     // else
227              {
2280                out.write(summary.getFindingType().getShortText());
229              }
2300             out.write("</a></td></tr>\n");
231           }
2320          out.write("</table>");
2330       }
234  
235  
236  
237     /**
238      * Holds all findings of a specific type.
239      * @author Andreas Mandel
240      */
2410    final class FindingSummary implements Comparable<FindingSummary>
242     {
2430       private final Map<String, FindingOccurrence> mOccurrences
244            = new HashMap<String, FindingOccurrence>();
245        private final Severity mSeverity;
246        private final Origin mOrigin;
247        private int mCounter;
2480       private boolean mFindingsHaveSameMessage = true;
249        private final String mFindingMessage;
250        private final FindingType mFindingType;
251  
252        /**
253         * Creates a new FindingSummary to collect findings similiar
254         * to the given finding.
255         * @param finding the reference Item for the types of findings
256         *     collected in this summary.
257         */
258        public FindingSummary (Item finding)
2590       {
2600          final String key = getKeyForFinding(finding);
2610          mFindingType = FindingType.fromString(finding.getFindingType());
2620          mSeverity = finding.getSeverity();
2630          mOrigin = finding.getOrigin();
2640          mFindingMessage = finding.getMessage();
2650          mFindings.put(key, this);
2660       }
267  
268        /**
269         * @return Returns the counter.
270         */
271        public int getCounter ()
272        {
2730          return mCounter;
274        }
275  
276        /**
277         * @return Returns the origin of the findings.
278         */
279        public Origin getOrigin ()
280        {
2810          return mOrigin;
282        }
283  
284        /**
285         * @return Returns the severity.
286         */
287        public Severity getSeverity ()
288        {
2890          return mSeverity;
290        }
291  
292        /**
293         * @return Returns the findingMessage.
294         */
295        public String getFindingMessage ()
296        {
2970          return mFindingMessage;
298        }
299        /**
300         * @return Returns the findingsHaveSameMessage.
301         */
302        public boolean isFindingsHaveSameMessage ()
303        {
3040          return mFindingsHaveSameMessage;
305        }
306  
307        /**
308         * @return Returns the finding type.
309         */
310        public FindingType getFindingType ()
311        {
3120          return mFindingType;
313        }
314  
315        /**
316         * @return Returns the occurrences.
317         */
318        public Map<String, FindingOccurrence> getOccurrences ()
319        {
3200          return Collections.unmodifiableMap(mOccurrences);
321        }
322  
323        public FindingOccurrence getOccurrence (FileSummary fileSummary)
324        {
3250          FindingOccurrence result =
326              getOccurrence(fileSummary.getFullClassName());
327  
3280          if (result == null)
329           {
3300             result = new FindingOccurrence(fileSummary);
331           }
332  
3330          return result;
334        }
335  
336        public void addFinding (Item finding, FileSummary summary)
337        {
3380          if (mFindingsHaveSameMessage)
339           {
3400             if (mFindingMessage == null)
341              {
3420                mFindingsHaveSameMessage
343                    = (finding.getMessage() == null);
344              }
345              else
346              {
3470                mFindingsHaveSameMessage
348                    = mFindingMessage.equals(finding.getMessage());
349              }
350           }
3510          getOccurrence(summary).addFinding(finding);
3520       }
353  
354        /** {@inheritDoc} */
355        public String toString ()
356        {
3570          return "[" + mFindingType + "(" + mSeverity
358                 + (mFindingsHaveSameMessage ? " " + mFindingMessage : "")
359                 + "): " + mOccurrences + "(" + mCounter + ")]";
360        }
361  
362  
363        /**
364         * {@inheritDoc}
365         * Be aware that the order (result of {@link #compareTo} can change
366         * if new findings are added.
367         * The order is from severe with most findings to info with
368         * fewer findings.
369         */
370        public int compareTo (FindingSummary other)
371        {
3720(2)         int result = -mSeverity.compareTo(other.mSeverity);
3730          if (result == 0)
374           {
3750             result = other.mCounter - mCounter;
376           }
3770          return result;
378        }
379  
380        private void addOccurrence (FindingOccurrence occurrence)
381        {
3820          mOccurrences.put(occurrence.getFullClassName(), occurrence);
3830       }
384  
385        private FindingOccurrence getOccurrence (String filename)
386        {
3870          return mOccurrences.get(filename);
388        }
389  
390        public String createFindingDetailFilename ()
391        {
3920          return "finding-" + getSeverity() + "-"
393              + getFindingType().getSymbol() + ".html";
394        }
395  
396 (3)(4)      public void createFindingTypeContent (Writer out)
397           throws IOException
398        {
399 (5)          // TODO: Handle global findings more nice
4000(6)         final FindingOccurrence[] allFindings
401                   = mOccurrences.values().toArray(new FindingOccurrence[0]);
402  
4030          Arrays.sort(allFindings);
404  
4050          out.write("<h1><a href='index.html'>View by Classes</a></h1>");
4060          out.write("<h1><a href='findings.html'>Findings - Overview</a></h1>");
407  
4080          out.write("<h1 title='");
4090          out.write(getFindingType().getSymbol());
4100          out.write("'>");
411  
4120          out.write(getSeverity().toString());
4130          out.write(" ");
4140          out.write(getFindingType().getShortText());
4150          out.write(" (");
4160          out.write(getOrigin().toString());
4170          out.write(")");
4180          out.write("</h1>\n");
419  
4200          if (isFindingsHaveSameMessage()
421                 && getFindingMessage() != null)
422           {
4230             out.write("<h2>");
4240             out.write(XmlUtil.escape(getFindingMessage()));
4250             out.write("</h2>\n");
426           }
427  
4280          if (getWikiPrefix() != null)
429           {
4300             out.write("<a href='" + getWikiPrefix()
431                    + getFindingType().getSymbol()
432                    + "'>Further info on the wiki.</a>\n");
433           }
434  
4350          out.write("<blockquote>\n");
4360          out.write(getFindingType().getDescription());
4370          out.write("</blockquote>\n");
438  
439  
4400          out.write("<table border='0' cellpadding='0' cellspacing='0' "
441                   + "width='95%' summary='Places of this finding.'>");
442  
443           for (final FindingSummary.FindingOccurrence
4440              occurrence : allFindings)
445           {
4460             out.write("<tr><td class='findingtype-counter'>");
4470             out.write(Integer.toString(occurrence.getFindings().size()));
4480             out.write("</td><td class='findingtype-class' width='100%'>");
449  //            out.write("<a href='");
450  //            out.write(occurrence.getHtmlLink());
451  //            out.write("'>");
4520             out.write(occurrence.getFullClassName());
4530             out.write("</td></tr>");
454  
4550             out.write("<tr><td class='findingtype-data' colspan='2'>");
456  
4570             final Iterator<Item> i = occurrence.getFindings().iterator();
4580             while (i.hasNext())
459              {
4600                final Item item = i.next();
4610                final String htmlLink = occurrence.getHtmlLink();
4620                if (htmlLink != null)
463                 {
4640                    out.write("<a href='");
4650                    out.write(occurrence.getHtmlLink());
4660                    out.write("#LINE");
4670                    out.write(Integer.toString(item.getLine()));
4680                    out.write("'>");
469                 }
4700                if (!isFindingsHaveSameMessage() && item.getMessage() != null)
471                 {
4720                   out.write(XmlUtil.escape(item.getMessage()));
473                 }
4740                out.write("&#160;[");
4750                out.write(Integer.toString(item.getLine()));
4760                if (item.getColumn() != 0)
477                 {
4780                   out.write(":");
4790                   out.write(Integer.toString(item.getColumn()));
480                 }
4810                out.write("]");
4820                if (htmlLink != null)
483                 {
4840                    out.write("</a>");
485                 }
4860                if (i.hasNext())
487                 {
4880                   out.write(", ");
489                 }
4900                if (!isFindingsHaveSameMessage() && item.getMessage() != null)
491                 {
4920                   out.write("<br />");
493                 }
4940             }
4950             out.write("</td></tr>\n");
496           }
4970          out.write("</table>\n");
4980       }
499  
500        /**
501         * Checks for the wiki prefix to be used.
502         * @return the wiki prefix to be used.
503         */
504        private String getWikiPrefix ()
505        {
5060          return System.getProperty(Java2Html.WIKI_BASE_PROPERTY);
507        }
508  
509  
510        /**
511         * A occurrence of a finding.
512         * This class encapsulates all findings of a single type in one file.
513         * Be aware that the order (result of {@link #compareTo} can change
514         * if new findings are added.
515         *
516         * @author Andreas Mandel
517         */
5180       final class FindingOccurrence implements Comparable<FindingOccurrence>
519        {
520           private final FileSummary mFileSummary;
5210          private final List<Item> mFindingsInFile = new ArrayList<Item>();
522  
523           private FindingOccurrence (FileSummary summary)
5240          {
5250             mFileSummary = summary;
5260             addOccurrence(this);
5270          }
528  
529           /**
530            * @return the name of the package of this class/file
531            */
532           public String getPackagename ()
533           {
5340             return mFileSummary.getPackage();
535           }
536  
537           public void addFinding (Item finding)
538           {
5390             mFindingsInFile.add(finding);
5400             mCounter++;
5410             mOverallCounter++;
5420          }
543  
544           public List<Item> getFindings ()
545           {
5460             return Collections.unmodifiableList(mFindingsInFile);
547           }
548  
549           /**
550            * @return ClassName including package.
551            */
552           public String getFullClassName ()
553           {
5540             return mFileSummary.getFullClassName();
555           }
556  
557           public String getClassName ()
558           {
5590             return mFileSummary.getClassName();
560           }
561  
562           public String getHtmlLink ()
563           {
5640             return mFileSummary.getHtmlLink();
565           }
566  
567           public int countFindingsInFile ()
568           {
5690             return mFindingsInFile.size();
570           }
571  
572           /** {@inheritDoc} */
573           public String toString ()
574           {
5750             return "[" + getClassName() + ": " + findingsToString()
576                    + "(" + mFindingsInFile.size() + ")]";
577           }
578  
579           public String findingsToString ()
580           {
5810             final StringBuilder sb = new StringBuilder();
5820             sb.append('{');
5830             final Iterator<Item> i = mFindingsInFile.iterator();
5840             while (i.hasNext())
585              {
5860                final Item finding = i.next();
5870                sb.append('@');
5880                sb.append(finding.getLine());
5890                sb.append(':');
5900                sb.append(finding.getColumn());
5910                if (i.hasNext())
592                 {
5930                   sb.append(", ");
594                 }
5950             }
5960             sb.append('}');
5970             return sb.toString();
598           }
599  
600           /**
601            * {@inheritDoc}
602            * Be aware that the order (result of {@link #compareTo} can change
603            * if new findings are added.
604            * The order is from most findings to fewer findings.
605            */
606           public int compareTo (FindingOccurrence o)
607           {
6080(7)            return o.mFindingsInFile.size() - this.mFindingsInFile.size();
609           }
610        }
611     }
612  }

Findings in this File

i (1) 116 : 0 Confusing to have methods org.jcoderz.phoenix.dependency.Clazz.getPackageName() and org.jcoderz.phoenix.report.FindingsSummary$FindingSummary$FindingOccurrence.getPackagename()
w (2) 372 : 0 org.jcoderz.phoenix.report.FindingsSummary$FindingSummary defines compareTo(FindingsSummary$FindingSummary) and uses Object.equals()
c (3) 396 : 7 Cyclomatic Complexity is 14 (max allowed is 12).
d (4) 396 : 7 Method length is 101 lines (max allowed is 100).
i (5) 399 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
i (6) 400 : 0 Method org.jcoderz.phoenix.report.FindingsSummary$FindingSummary.createFindingTypeContent(Writer) uses Collection.toArray() with zero-length array argument
w (7) 608 : 0 org.jcoderz.phoenix.report.FindingsSummary$FindingSummary$FindingOccurrence defines compareTo(FindingsSummary$FindingSummary$FindingOccurrence) and uses Object.equals()