Project Report: fawkez

Packagesummary org.jcoderz.phoenix.report

org.jcoderz.phoenix.report.GenericReportReader

LineHitsNoteSource
1  /*
2   * $Id: SourceDirectoryReader.java 1408 2009-04-14 16:06:46Z 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.BufferedReader;
36  import java.io.File;
37  import java.io.FileInputStream;
38  import java.io.FileNotFoundException;
39  import java.io.FileReader;
40  import java.io.IOException;
41  import java.io.InputStream;
42  import java.io.Reader;
43  import java.util.ArrayList;
44  import java.util.Collections;
45  import java.util.HashMap;
46  import java.util.Iterator;
47  import java.util.List;
48  import java.util.Map;
49  import java.util.logging.Level;
50  import java.util.logging.Logger;
51  import java.util.regex.Matcher;
52  import java.util.regex.Pattern;
53  
54  import javax.xml.bind.JAXBException;
55  
56  import org.jcoderz.commons.util.Assert;
57  import org.jcoderz.commons.util.Constants;
58  import org.jcoderz.commons.util.IoUtil;
59  import org.jcoderz.commons.util.JaxbUtil;
60  import org.jcoderz.commons.util.ObjectUtil;
61  import org.jcoderz.commons.util.StringUtil;
62  import org.jcoderz.commons.util.JaxbUtil.UnmarshalResult;
63  import org.jcoderz.phoenix.report.ftf.jaxb.FindingDescription;
64  import org.jcoderz.phoenix.report.ftf.jaxb.FindingTypeFormat;
65  import org.jcoderz.phoenix.report.jaxb.Item;
66  import org.xml.sax.InputSource;
67  
68  /**
69   * Reads reports with format definitions described in the
70   * finding-type-format-definition.xds.
71   *
72   * To find the finding type format definition for requested format
73   * the following locations are used:
74   *
75   * The name is converted to lower case.
76   *
77   * A file <i>name</i>.xml is searched in the
78   * <code>org.jcoderz.phoenix.report.ftf</code> package. If
79   * this is not found the file is searched in the <code>ftf</code>
80   * directory. The directory must be available through the classpath.
81   *
82   *
83   * @author Andreas Mandel
84   *
85   */
86  public final class GenericReportReader implements ReportReader
87  {
88      private static final int MAX_DEBUG_TEXT_CHARS = 100;
890     private static final String CLASSNAME
90          = GenericReportReader.class.getName();
910     private static final Logger logger = Logger.getLogger(CLASSNAME);
92  
930     private static final Pattern CODE_LINE_PATTERN
94          = Pattern.compile("^.*$", Pattern.MULTILINE);
95  
960     private static final Pattern CARET_LINE_PATTERN
97          = Pattern.compile("^\\s*\\^$", Pattern.MULTILINE);
98  
990     private static final Map<Origin, GenericReportReader> GENERIC_REPORT_TYPES
100          = new HashMap<Origin, GenericReportReader>();
101  
102  
1030     private final List<GenericFindingType> mFindingTypes
104          = new ArrayList<GenericFindingType>();
105  
106      private Map<ResourceInfo, List<Item>> mItems;
107  
108      private SourceFile mSourceFile;
109  
110      private final Pattern mMessagePattern;
111      private final FindingTypeFormat mFindingTypeFormatDescription;
112  
113      private final int mTextPos;
114      private final Origin mOrigin;
115      private final int mFilePos;
116      private final int mLineStart;
117      private final Severity mDefaultSeverity;
118  
1190     private Matcher mRootMatcher = null;
120  
121      private GenericReportReader (Origin type)
122          throws JAXBException
1230     {
1240         mOrigin = type;
1250         mFindingTypeFormatDescription = loadFormatDescription(type);
1260         initializeFindingTypes();
1270         final FindingDescription root
128              = mFindingTypeFormatDescription.getRootType();
1290         mMessagePattern
130              = Pattern.compile(root.getPattern(),
131                  Pattern.MULTILINE);
1320         mTextPos = Integer.parseInt(root.getTextPos());
1330         mFilePos = Integer.parseInt(root.getFilenamePos());
1340         mLineStart = root.isSetLineStartPos()
135              ? Integer.parseInt(root.getLineStartPos()) : -1;
1360         mDefaultSeverity = root.isSetSeverity()
137              ? root.getSeverity() : Severity.CODE_STYLE;
1380     }
139  
140      /**
141       * Initializes the selected finding type.
142       * Might return <code>null</code> if the initialization fails.
143 (1)     * CHECKME: Should return a null object?
144       * @param findingType the type to load.
145       * @return the loaded finding type.
146       */
147      public static GenericReportReader initialize (Origin findingType)
148      {
1490         GenericReportReader result = null;
1500         synchronized (GENERIC_REPORT_TYPES)
151          {
1520             if (!GENERIC_REPORT_TYPES.containsKey(findingType))
153              {
154                  try
155                  {
1560                     result = new GenericReportReader(findingType);
157                  }
1580                 catch (Exception ex)
159                  {
160 (2)                    // TODO: collect this an add it to the findings map later!
1610                     logger.log(Level.WARNING,
162                          "Could not load finding type for '" + findingType
163                          + "' failed with " + ex.getMessage() + ".", ex);
1640                 }
1650(3)                GENERIC_REPORT_TYPES.put(findingType, result);
166              }
1670             result = GENERIC_REPORT_TYPES.get(findingType);
1680         }
1690         return result;
170      }
171  
172      private static FindingTypeFormat loadFormatDescription (Origin type)
173          throws JAXBException
174      {
1750         FindingTypeFormat findingTypeFormatDescription = null;
1760         InputStream in = null;
177          try
178          {
1790             final String filename
180                  = type.toString().toLowerCase(Constants.SYSTEM_LOCALE)
181                      + ".xml";
1820             in = GenericReportReader.class.getResourceAsStream(
183                  "ftf/" + filename);
1840             if (in == null)
185              {
1860                 in = GenericReportReader.class.getResourceAsStream(
187                      "/ftf/" + filename);
188              }
1890             if (in == null)
190              {
191                  try
192                  {
1930                     in = new FileInputStream(filename);
194                  }
1950                 catch (FileNotFoundException ex)
196                  {
197                      // in = null;
1980                 }
199              }
2000             Assert.notNull(in, "report type description " + type);
2010             final UnmarshalResult unmarshal
202                  = JaxbUtil.unmarshal(new InputSource(in),
203                      "org.jcoderz.phoenix.report.ftf.jaxb");
204  
2050             findingTypeFormatDescription
206                  = (FindingTypeFormat) unmarshal.getParsedData();
207          }
208          finally
209          {
2100             IoUtil.close(in);
2110         }
2120         return findingTypeFormatDescription;
213      }
214  
215      /** {@inheritDoc} */
216      public void parse (File f)
217          throws JAXBException
218      {
219          try
220          {
2210             mSourceFile = new SourceFile(f);
2220             mRootMatcher = mMessagePattern.matcher(mSourceFile.getContent());
223          }
2240         catch (IOException ex)
225          {
2260             throw new JAXBException("Failed to read '" + f + "'.", ex);
2270         }
2280     }
229  
230      /** {@inheritDoc} */
231      public void merge (Map<ResourceInfo, List<Item>> items)
232          throws JAXBException
233      {
2340         mItems = items;
2350         while (!mSourceFile.readFully())
236          {
2370             parseNext();
238          }
2390     }
240  
241      /**
242       * Reads the given message and tries to find a matching finding type.
243       * @param message the message to read.
244       * @return the finding type matching to the message, or null if no such
245       *   type was found.
246       * @throws JAXBException if item creation fails.
247       */
248      public Item detectFindingTypeForMessage (String message)
249          throws JAXBException
250      {
2510        Item result = null;
2520        for (final GenericFindingType type : mFindingTypes)
253         {
2540            result = type.createItem(mSourceFile, message);
2550            if (result != null)
256             {
2570               if (type.isSourceColumnByCaret())
258                {
2590                   addPositionByCaret(result);
260                }
261                break;
262             }
263         }
2640        if (logger.isLoggable(Level.FINE))
265         {
2660            logger.fine("For text: '"
267                 + StringUtil.trimLength(message, MAX_DEBUG_TEXT_CHARS)
268                 + "' matched finding: "
269                 + (result == null ? "null" : result.getFindingType()
270                 + "'. End at " + mSourceFile.getPos()));
271         }
2720        return result;
273      }
274  
275      private void addPositionByCaret (final Item i)
276      {
2770         final String text
278              = mSourceFile.getContent().substring(mSourceFile.getPos());
2790         final Matcher codeMat
280              = CODE_LINE_PATTERN.matcher(text);
2810         if (codeMat.lookingAt())
282          {
2830             final String textAfterCode
284                  = mSourceFile.getContent().substring(
285                      mSourceFile.getPos() + codeMat.end() + 1);
2860             final Matcher caretMat
287                  = CARET_LINE_PATTERN.matcher(textAfterCode);
2880             if (caretMat.lookingAt())
289              {
2900                 i.setColumn(caretMat.end());
2910                 mSourceFile.setPos(
292                      mSourceFile.getPos()
293                      + codeMat.end() + 1
294                      + caretMat.end() + 1);
295              }
296              else
297              {
2980                 logger.fine("Caret defined but not found for '"
299                      + i.getFindingType()
300                      + "' Code Line: '" + codeMat + "' caretLine: '"
301                      + caretMat + "'. text: '"
302                      + StringUtil.trimLength(
303                          textAfterCode, MAX_DEBUG_TEXT_CHARS) + "'.");
304              }
3050         }
306          else
307          {
3080             logger.fine("Caret defined but not found for '"
309                  + i.getFindingType()
310                  + "' Code Line: '" + codeMat + "'. text: '"
311                  + StringUtil.trimLength(
312                      text, MAX_DEBUG_TEXT_CHARS) + "'.");
313          }
3140     }
315  
316 (4)    private void parseNext ()
317          throws JAXBException
318      {
3190         if (mRootMatcher.find())
320          {
3210             final String text = mRootMatcher.group(mTextPos);
3220             if (logger.isLoggable(Level.FINE))
323              {
3240                 logger.fine("Main pattern matched for: '"
325                      + StringUtil.trimLength(text, MAX_DEBUG_TEXT_CHARS)
326                      + "'. End at " + mRootMatcher.end());
327              }
3280             mSourceFile.setPos(mRootMatcher.start(mTextPos));
3290             final Item item = detectFindingTypeForMessage(text);
3300             if (item == null)
331              {
3320                 final int pos
333                      = mSourceFile.getContent().indexOf(
334                          '\n', mSourceFile.getPos());
3350                 if (pos != -1)
336                  {
3370                     mSourceFile.setPos(pos + 1);
338                  }
339                  else
340                  {
3410                     mSourceFile.setPos(mSourceFile.getContent().length());
342                  }
3430             }
344              else
345              {
3460                 item.setOrigin(mOrigin);
3470                 if (!item.isSetSeverity())
348                  {
3490                     item.setSeverity(mDefaultSeverity);
350                  }
3510                 if (!item.isSetLine() && mLineStart != -1
352                      && mRootMatcher.group(mLineStart) != null)
353                  {
3540                     item.setLine(
355                          Integer.parseInt(mRootMatcher.group(mLineStart)));
356                  }
3570                 if (!item.isSetFindingType())
358                  {
3590                     item.setFindingType(mOrigin.toString());
360                  }
3610                 if (!item.isSetMessage())
362                  {
3630                     item.setMessage(mRootMatcher.group(mTextPos));
364                  }
3650                 if (mFindingTypeFormatDescription.getRootType().isGlobal())
366                  {
3670                     item.setGlobal(true);
368                  }
3690                 addItemToResource(mRootMatcher.group(mFilePos), item);
370              }
3710             mRootMatcher.region(
372                  mSourceFile.getPos(), mSourceFile.getContent().length());
3730         }
374          else
375          {
3760             if (logger.isLoggable(Level.FINE))
377              {
3780                 logger.fine("No match after " + mSourceFile.getPos()
379                      + " '" + StringUtil.trimLength(
380                          mSourceFile.getContent().substring(
381                              mSourceFile.getPos()),
382                              MAX_DEBUG_TEXT_CHARS));
383              }
384              // set pos to end of file
3850             mSourceFile.setPos(mSourceFile.getContent().length());
386          }
3870     }
388  
389      private void addItemToResource (String resourceFilename, Item item)
390      {
3910         final ResourceInfo info = ResourceInfo.lookup(resourceFilename);
3920         if (info != null || item.isGlobal())
393          {
394              final List<Item> l;
3950             if (mItems.containsKey(info))
396              {
3970                 l = mItems.get(info);
398              }
399              else
400              {
4010                 l = new ArrayList<Item>();
4020                 mItems.put(info, l);
403              }
404              // sometimes javadoc reports the same thing twice...
4050             final Iterator<Item> i = l.iterator();
4060             while (i.hasNext())
407              {
4080                 final Item it = i.next();
4090                 if (it.getLine() == item.getLine()
410                      && it.getColumn() == item.getColumn()
411                      && it.getOrigin() == item.getOrigin()
412                      && ObjectUtil.equals(it.getMessage(), item.getMessage()))
413                  {
4140                     i.remove();
4150                     break;
416                  }
4170             }
4180             l.add(item);
4190         }
420          else
421          {
4220             logger.finer("Ignore findings for resource '"
423                  + resourceFilename + "' type was "
424                  + item.getFindingType() + ".");
425          }
4260     }
427  
428      private void initializeFindingTypes ()
429      {
4300         final FindingDescription root
431              = mFindingTypeFormatDescription.getRootType();
4320         final List<FindingDescription> findingTypes
433 (5)            = mFindingTypeFormatDescription.getFindingType();
4340         for (FindingDescription findingDesc : findingTypes)
435          {
4360             final GenericFindingType gft
437                  = new GenericFindingType(root, findingDesc);
4380             mFindingTypes.add(gft);
4390         }
4400         Collections.sort(
441              mFindingTypes, new GenericFindingType.OrderByPriority());
4420     }
443  
444  
445      static final class SourceFile
446      {
447          private final File mFile;
448          private final String mContent;
449          private int mPos;
450  
451          public SourceFile (File file)
452              throws IOException
4530         {
4540             mFile = file;
4550             Reader in = null;
4560             Reader buffered = null;
457              try
458              {
4590                 in = new FileReader(file);
4600                 buffered = new BufferedReader(in);
4610                 mContent = IoUtil.readFullyNormalizeNewLine(buffered);
462              }
463              finally
464              {
4650                 IoUtil.close(buffered);
4660                 IoUtil.close(in);
4670             }
4680             mPos = 0;
4690         }
470  
471          /**
472           * @return the pos
473           */
474          public int getPos ()
475          {
4760             return mPos;
477          }
478  
479          /**
480           * @param pos the pos to set
481           */
482          public void setPos (int pos)
483          {
4840             mPos = pos;
4850         }
486  
487          /**
488           * @return the file
489           */
490          public File getFile ()
491          {
4920             return mFile;
493          }
494  
495          /**
496           * @return the content
497           */
498          public String getContent ()
499          {
5000             return mContent;
501          }
502  
503          public boolean readFully ()
504          {
5050             return mPos >= mContent.length();
506          }
507      }
508  }

Findings in this File

i (1) 143 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
i (2) 160 : 0 Comment matches to-do format '(TODO|FIXME|CHECKME)'.
w (3) 165 : 0 class org.jcoderz.phoenix.report.GenericReportReader defines static field that appears to allow memory bloat
c (4) 316 : 5 Cyclomatic Complexity is 13 (max allowed is 12).
d (5) 433 : 59 [unchecked] unchecked conversion found : java.util.List required: java.util.List<org.jcoderz.phoenix.report.ftf.jaxb.FindingDescription>