Project Report: fawkez

Packagesummary org.jcoderz.phoenix.report

org.jcoderz.phoenix.report.ReportMerger

LineHitsNoteSource
1  /*
2   * $Id: ReportMerger.java 1533 2009-07-06 20:20:15Z 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.File;
36  import java.io.FileNotFoundException;
37  import java.io.FileOutputStream;
38  import java.io.IOException;
39  import java.util.ArrayList;
40  import java.util.Iterator;
41  import java.util.List;
42  import java.util.logging.Level;
43  import java.util.logging.Logger;
44  
45  import javax.xml.bind.JAXBContext;
46  import javax.xml.bind.JAXBException;
47  import javax.xml.bind.Marshaller;
48  import javax.xml.bind.PropertyException;
49  import javax.xml.transform.Transformer;
50  import javax.xml.transform.TransformerException;
51  import javax.xml.transform.TransformerFactory;
52  import javax.xml.transform.stream.StreamResult;
53  import javax.xml.transform.stream.StreamSource;
54  
55  import org.jcoderz.commons.ArgumentMalformedException;
56  import org.jcoderz.commons.types.Date;
57  import org.jcoderz.commons.util.Assert;
58  import org.jcoderz.commons.util.FileUtils;
59  import org.jcoderz.commons.util.IoUtil;
60  import org.jcoderz.commons.util.LoggingUtils;
61  import org.jcoderz.commons.util.ObjectUtil;
62  import org.jcoderz.commons.util.StringUtil;
63  import org.jcoderz.phoenix.report.jaxb.Item;
64  import org.jcoderz.phoenix.report.jaxb.ObjectFactory;
65  import org.jcoderz.phoenix.report.jaxb.Report;
66  
67  /**
68   * Provides merging and filtering of various jcoderz-report.xml files.
69   * It combines parts of the functions from ReportNormalizer and XmlMergeAntTask.
70   *
71   * @author Michael Rumpf
72   */
730 public class ReportMerger
74  {
75  
76     /** The Constant CLASSNAME. */
77100    private static final String CLASSNAME = ReportNormalizer.class.getName();
78  
79     /** The Constant logger. */
80100    private static final Logger logger = Logger.getLogger(CLASSNAME);
81  
82     /** The length of an unique part of a c&p finding message. */
83100    private static final int CPD_UNIQUE_STRING_LENGTH
84         = "Copied and pasted code. 341 equal".length();
85     
86     /** The log level. */
87     private Level mLogLevel;
88  
89     /** The out file. */
900    private File mOutFile = null;
91  
92     /** The reports. */
930    private final List<File> mReports = new ArrayList<File>();
94  
95     /** The filters. */
960    private final List<File> mFilters = new ArrayList<File>();
97  
98     /** The old Report. */
99     private File mOldReport;
100  
101     /** The old Report. */
1020    private final Date mReportDate = Date.now();
103  
104     /**
105      * Merge input reports.
106      * @throws JAXBException if a xml handling error occurs.
107      * @throws FileNotFoundException in case of an IO issue.
108      */
109     public void merge ()
110         throws JAXBException, FileNotFoundException
111     {
1120      logger.log(Level.FINE, "Merging jcoderz-report.xml files...");
113       // merge the reports
1140      final Report mergedReport = new ObjectFactory().createReport();
1150      for (final File reportFile : mReports)
116       {
1170         logger.log(Level.FINE, "Report: " + reportFile);
118          try
119          {
1200            final Report report = (Report) new ObjectFactory()
121                   .createUnmarshaller().unmarshal(reportFile);
1220(1)           mergedReport.getFile().addAll(report.getFile());
123          }
1240         catch (JAXBException ex)
125          {
126 (2)           // TODO: ADD ISSUE AS system ITEM TO THE REPORT
1270            ex.printStackTrace();
1280         }
129       }
1300      writeResult(mergedReport, mOutFile);
1310    }
132  
133  
134     /**
135      * Filters the report XML file using the JDK XSL processor.
136      * @throws TransformerException if the transformation fails.
137      * @throws IOException if an io operation fails.
138      */
139     public void filter () throws TransformerException, IOException
140     {
1410        logger.log(Level.FINE, "Filtering jcoderz-report.xml files...");
1420        for (final File filterFile : mFilters)
143         {
1440            logger.log(Level.FINE, "Filter: " + filterFile);
1450            final TransformerFactory tFactory
146                 = TransformerFactory.newInstance();
147  
1480            final Transformer transformer
149                 = tFactory.newTransformer(new StreamSource(filterFile));
150  
1510            final File tempOutputFile
152                 = new File(mOutFile.getCanonicalPath() + ".tmp");
1530            FileUtils.createNewFile(tempOutputFile);
154  
1550            final FileOutputStream out = new FileOutputStream(tempOutputFile);
1560            transformer.transform(new StreamSource(mOutFile),
157                 new StreamResult(out));
1580            IoUtil.close(out);
1590            FileUtils.copyFile(tempOutputFile, mOutFile);
1600            FileUtils.delete(tempOutputFile);
1610        }
1620    }
163  
164     /**
165      * Searches for new findings based on the old jcReport and increases the
166      * severity of such findings to NEW. 
167      */
168     public void flagNewFindings ()
169     {
1700        logger.log(Level.FINE, "Searching for NEW findings...");
171         try
172         {
1730            final Report currentReport
174                 = (Report) new ObjectFactory().createUnmarshaller().unmarshal(
175                     mOutFile);
1760            final Report oldReport
177                 = (Report) new ObjectFactory().createUnmarshaller().unmarshal(
178                     mOldReport);
179             for (org.jcoderz.phoenix.report.jaxb.File newFile
1800                : (List<org.jcoderz.phoenix.report.jaxb.File>)
181 (3)                   currentReport.getFile())
182             {
1830                final org.jcoderz.phoenix.report.jaxb.File oldFile
184                     = findFile(newFile, oldReport);
1850                if (oldFile != null)
186                 {
1870                    findNewFindings(newFile, oldFile);
188                 }
189                 else
190                 {
1910(4)                   flaggAllAsNew(newFile.getItem());
192                 }
1930            }
194             
1950            writeResult(currentReport, mOutFile);
196         }
1970        catch (Exception ex)
198         {
1990            logger.log(Level.WARNING,
200                 "Failed to flagNewFindings. Cause " + ex.getMessage(), ex);
2010        }
2020    }
203  
204      private void findNewFindings (org.jcoderz.phoenix.report.jaxb.File newFile,
205          org.jcoderz.phoenix.report.jaxb.File oldFile)
206      {
2070         final List<Item> newFindings
208 (5)            = new ArrayList<Item>((List<Item>) newFile.getItem());
2090         final List<Item> oldFindings
210 (6)            = new ArrayList<Item>((List<Item>) oldFile.getItem());
211  
2120         filterLowSeverity(newFindings);
2130         filterLowSeverity(oldFindings);
2140         filterFullMatches(newFindings, oldFindings);
2150         filterPartialMatches(newFindings, oldFindings);
216  
217          // the rest...
2180         flaggAllAsNew(newFindings);
2190         for (Item item : oldFindings)
220          {
2210(7)            addAsOld(newFile.getItem(), item);
222          }
223          
2240     }
225  
226      private void flaggAllAsNew (final List<Item> newFindings)
227      {
2280         for (Item item : newFindings)
229          {
2300             if (item.getSeverity().getPenalty() > 0
231                  && item.getSeverity() != Severity.COVERAGE)
232              {
2330                 flagAsNew(item);
234              }
235          }
2360     }
237  
238      private void addAsOld (List<Item> newFindings, Item item)
239      {
2400         if (item.getSeverity().getPenalty() > 0
241              && item.getSeverity() != Severity.COVERAGE)
242          {
2430             item.setSeverity(Severity.OK);
2440             item.unsetNew();
2450             item.setOld(true);
2460             newFindings.add(item);
247          }
2480     }
249  
250  
251      private void filterFullMatches (final List<Item> newFindings,
252          final List<Item> oldFindings)
253      {
254          // Filter 100% matches:
2550         final Iterator<Item> newIterator = newFindings.iterator();
2560         while (newIterator.hasNext())
257          {
2580             final Item newItem = newIterator.next();
2590             final Iterator<Item> oldIterator = oldFindings.iterator();
2600             while (oldIterator.hasNext())
261              {
2620                 final Item oldItem = oldIterator.next();
2630                 if (isSameFinding(newItem, oldItem))
264                  {
2650                     newItem.setSince(oldItem.getSince());
2660                     newIterator.remove();
2670                     oldIterator.remove();
2680                     break;
269                  }
2700             }
2710         }
2720     }
273  
274  
275      /* private */ static boolean isSameFinding (Item newItem, Item oldItem)
276      {
277          final boolean result;
278100         if (oldItem.getFindingType().equals(newItem.getFindingType()))
279          {
280100             if (oldItem.getOrigin().equals(Origin.CPD))
281              {
282                  // Fuzzy compare CPD Findings
283                  // see also http://www.jcoderz.org/fawkez/ticket/71
28480                 result = oldItem.getLine() == newItem.getLine()
285                      && oldItem.getMessage().regionMatches(
286                          0, newItem.getMessage(), 0, CPD_UNIQUE_STRING_LENGTH);
287              }
288              else
289              {
2900                 result = oldItem.getLine() == newItem.getLine()
291                      && oldItem.getColumn() == newItem.getColumn()
292                      && oldItem.getMessage().equals(newItem.getMessage())
293                      && oldItem.getCounter() <= newItem.getCounter();
294              }
295          }
296          else
297          {
2980             result = false;
299          }
300100         return result;
301      }
302  
303      private void filterPartialMatches (final List<Item> newFindings,
304          final List<Item> oldFindings)
305      {
306          // Filter matches that 'moved' within the file.
307          // There is for sure a better algorithm possible..
3080         final Iterator<Item> newIterator = newFindings.iterator();
3090         while (newIterator.hasNext())
310          {
3110             final Item newItem = newIterator.next();
3120             final Iterator<Item> oldIterator = oldFindings.iterator();
3130             while (oldIterator.hasNext())
314              {
3150                 final Item oldItem = oldIterator.next();
3160                 if (isPartialSameFinding(newItem, oldItem))
317                  {
3180                     newItem.setSince(oldItem.getSince());
3190                     newIterator.remove();
3200                     oldIterator.remove();
3210                     break;
322                  }
3230             }
3240         }
3250     }
326  
327  
328      private boolean isPartialSameFinding (Item newItem, Item oldItem)
329      {
330          final boolean result;
3310         if (oldItem.getFindingType().equals(newItem.getFindingType()))
332          {
3330             if (oldItem.getOrigin().equals(Origin.CPD))
334              {
335                  // Fuzzy compare CPD Findings
336                  // see also http://www.jcoderz.org/fawkez/ticket/71
337                  // The or is by intention due to resistant findings
338                  // reported as new frequently.
3390                 result = oldItem.getLine() == newItem.getLine()
340                      || oldItem.getMessage().regionMatches(
341                          0, newItem.getMessage(), 0, CPD_UNIQUE_STRING_LENGTH);
342              }
343              else
344              {
3450                 result = oldItem.getMessage().equals(newItem.getMessage())
346                      && oldItem.getCounter() <= newItem.getCounter();
347              }
348          }
349          else
350          {
3510             result = false;
352          }
3530         return result;
354      }
355  
356      private void filterLowSeverity (final List<Item> newFindings)
357      {
3580         final Iterator<Item> i = newFindings.iterator();
3590         while (i.hasNext())
360          {
3610             final Item item = i.next();
3620             if (item.getSeverity().getPenalty() == 0
363                  || item.getSeverity() == Severity.COVERAGE)
364              {
3650                 i.remove();
366              }
3670         }
3680     }
369  
370      private void flagAsNew (Item item)
371      {
3720         item.unsetOld();
3730         item.setNew(true);
3740         item.setSince(mReportDate);
3750     }
376  
377  
378     // This could be done faster, might be restructure the data first for
379     // faster lookup.
380     private org.jcoderz.phoenix.report.jaxb.File findFile (
381         org.jcoderz.phoenix.report.jaxb.File newFile, Report oldReport)
382     {
3830        final String className = newFile.getClassname();
3840        final String packageName = newFile.getPackage();
3850        final String fileName = newFile.getName();
3860        org.jcoderz.phoenix.report.jaxb.File result = null;
387         for (org.jcoderz.phoenix.report.jaxb.File file
3880(8)           : (List<org.jcoderz.phoenix.report.jaxb.File>) oldReport.getFile())
389         {
3900            if (ObjectUtil.equals(file.getName(), fileName)
391                 || (!StringUtil.isEmptyOrNull(className)
392                     && packageName != null
393                     && ObjectUtil.equals(file.getClassname(), className)
394                     && ObjectUtil.equals(file.getPackage(), packageName)))
395             {
3960                result = file;
3970                break;
398             }
399         }
4000        return result;
401     }
402  
403  
404     /**
405      * Parses the arguments.
406      *
407      * @param args the args
408      */
409     private void parseArguments (String[] args)
410     {
411        try
412        {
4130          for (int i = 0; i < args.length; )
414           {
4150             logger.fine("Parsing argument '" + args[i] + "' = '"
416                    + args[i + 1] + "'");
417  
4180             if ("-jcreport".equals(args[i]))
419              {
4200                addReport(new File(args[i + 1]));
421              }
4220             else if ("-filter".equals(args[i]))
423              {
4240                addFilter(new File(args[i + 1]));
425              }
4260             else if ("-old".equals(args[i]))
427              {
4280                setOldFile(new File(args[i + 1]));
429              }
4300             else if ("-loglevel".equals(args[i]))
431              {
4320                 setLogLevel(Level.parse(args[i + 1]));
433              }
4340             else if ("-out".equals(args[i]))
435              {
4360                 setOutFile(new File(args[i + 1]));
437              }
438              else
439              {
4400                throw new IllegalArgumentException(
441                         "Invalid argument '" + args[i] + "'");
442              }
443  
4440             ++i;
4450             ++i;
446           }
447        }
4480       catch (IndexOutOfBoundsException e)
449        {
4500          final IllegalArgumentException ex = new IllegalArgumentException(
451              "Missing value for " + args[args.length - 1]);
4520          ex.initCause(e);
4530          throw ex;
454        }
4550       catch (IOException e)
456        {
4570          final IllegalArgumentException ex = new IllegalArgumentException(
458              "Wrong out folder " + args[args.length - 1]);
4590          ex.initCause(e);
4600          throw ex;
4610       }
4620    }
463  
464     /**
465      * The main method.
466      *
467      * @param args the arguments
468      * @throws Exception in case of a technical issue.
469      */
470     public static void main (String[] args)
471         throws Exception
472     {
4730       final ReportMerger rm = new ReportMerger();
4740       rm.parseArguments(args);
4750       rm.merge();
4760       rm.filter();
4770    }
478  
479      /**
480       * Adds the report.
481       * @param report the report
482       */
483      public void addReport (File report)
484      {
4850         mReports.add(report);
4860     }
487  
488      /**
489       * Adds the filter.
490       * @param filter the filter
491       */
492      public void addFilter (File filter)
493      {
4940         mFilters.add(filter);
4950     }
496  
497      /**
498       * Gets the log level.
499       *
500       * @return the log level
501       */
502      public Level getLogLevel ()
503      {
5040         return mLogLevel;
505      }
506  
507  
508      /**
509       * Sets the log level.
510       *
511       * @param logLevel the new log level
512       */
513      public void setLogLevel (Level logLevel)
514      {
5150         mLogLevel = logLevel;
5160         LoggingUtils.setGlobalHandlerLogLevel(mLogLevel);
5170         logger.fine("Setting log level: " + mLogLevel);
5180         logger.setLevel(mLogLevel);
5190     }
520  
521  
522      /**
523       * Gets the out file.
524       *
525       * @return the out file
526       */
527      public File getOutFile ()
528      {
5290         return mOutFile;
530      }
531  
532  
533      /**
534       * Set the old report to compare with.
535       * @param file old report file.
536       * @throws IOException if the file name conversion fails
537       */
538      public void setOldFile (File file)
539          throws IOException
540      {
5410         Assert.notNull(file, "file");
5420         if (mOldReport != null)
543          {
5440             throw new ArgumentMalformedException("old", file,
545                  "Old Report File has already set to '" + mOldReport + "'.");
546          }
5470         mOldReport = file.getCanonicalFile();
5480     }
549      
550      /**
551       * Sets the out file.
552       *
553       * @param outFile the new out file
554       *
555       * @throws IOException Signals that an I/O exception has occurred.
556       */
557      public void setOutFile (File outFile)
558          throws IOException
559      {
5600         if (mOutFile != null)
561          {
5620             throw new ArgumentMalformedException("outFile", outFile,
563                  "Out File already set to '" + mOutFile + "'.");
564          }
5650         mOutFile = outFile;
5660         if (mOutFile.isDirectory())
567          {
5680             FileUtils.mkdirs(mOutFile);
5690             mOutFile = new File(mOutFile,
570                  ReportNormalizer.JCODERZ_REPORT_XML).getCanonicalFile();
571          }
572          else
573          {
5740            mOutFile = mOutFile.getCanonicalFile();
575          }
576  
5770     }
578  
579      private void writeResult (final Report mergedReport, File outFile)
580          throws JAXBException, PropertyException, FileNotFoundException
581      {
582          // create the file
5830          final JAXBContext mJaxbContext
584               = JAXBContext.newInstance("org.jcoderz.phoenix.report.jaxb",
585             this.getClass().getClassLoader());
5860          final Marshaller marshaller = mJaxbContext.createMarshaller();
5870          marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
588                   Boolean.TRUE);
5890          final FileOutputStream out = new FileOutputStream(outFile);
590           try
591           {
5920              marshaller.marshal(mergedReport, out);
593           }
594           finally
595           {
5960              IoUtil.close(out);
5970          }
5980     }
599      
600  
601  }

Findings in this File

d (1) 122 : 41 [unchecked] unchecked call to addAll(java.util.Collection<? extends E>) as a member of the raw type java.util.List
i (2) 126 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
c (3) 181 : 41 [unchecked] unchecked cast found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.File>
d (4) 191 : 49 [unchecked] unchecked conversion found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.Item>
c (5) 208 : 63 [unchecked] unchecked cast found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.Item>
c (6) 210 : 63 [unchecked] unchecked cast found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.Item>
d (7) 221 : 37 [unchecked] unchecked conversion found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.Item>
c (8) 388 : 76 [unchecked] unchecked cast found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.jaxb.File>