/* * $Id$ * * Copyright 2006, The jCoderZ.org Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials * provided with the distribution. * * Neither the name of the jCoderZ.org Project nor the names of * its contributors may be used to endorse or promote products * derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jcoderz.phoenix.report; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.logging.Logger; import javax.xml.bind.JAXBException; import org.jcoderz.phoenix.findbugs.jaxb.BugCollection; import org.jcoderz.phoenix.findbugs.jaxb.BugInstanceType; import org.jcoderz.phoenix.findbugs.jaxb.Class; import org.jcoderz.phoenix.findbugs.jaxb.Field; import org.jcoderz.phoenix.findbugs.jaxb.Int; import org.jcoderz.phoenix.findbugs.jaxb.Method; import org.jcoderz.phoenix.findbugs.jaxb.SourceLine; import org.jcoderz.phoenix.findbugs.jaxb.SourceLineType; import org.jcoderz.phoenix.report.jaxb.Item; import org.jcoderz.phoenix.report.jaxb.ObjectFactory; /** * * @author Michael Griffel */ public final class FindBugsReportReader extends AbstractReportReader { /** JAXB context path. */ public static final String FINDBUGS_JAXB_CONTEXT_PATH = "org.jcoderz.phoenix.findbugs.jaxb"; private static final String CLASSNAME = FindBugsReportReader.class.getName(); private static final Logger logger = Logger.getLogger(CLASSNAME); private BugCollection mReportDocument; FindBugsReportReader () throws JAXBException { super(FINDBUGS_JAXB_CONTEXT_PATH); } /** {@inheritDoc} */ public void parse (File f) throws JAXBException { try { mReportDocument = (BugCollection) getUnmarshaller().unmarshal( new JCoverageInputStream(new FileInputStream(f))); } catch (IOException e) { throw new JAXBException("Cannot read JCoverage report", e); } } /** {@inheritDoc} */ public Map getItems () throws JAXBException { final Map itemMap = new HashMap(); final List bugInstances = mReportDocument.getBugInstance(); logger.fine("Found #" + bugInstances.size() + " FindBugs bug instances!"); final List sourceDirs = mReportDocument.getProject().getSrcDir(); logger.finer("Using source dir '" + sourceDirs + "'"); for (final Iterator iterator = bugInstances.iterator(); iterator.hasNext();) { final BugInstanceType bugInstance = (BugInstanceType) iterator.next(); final List list = bugInstance.getClassOrFieldOrMethod(); final Item item = new ObjectFactory().createItem(); final List objectMessageList = new ArrayList(); item.setMessage(bugInstance.getLongMessage()); boolean topLevelSourceLineRead = false; for (final Iterator iter = list.iterator(); iter.hasNext();) { final Object element = iter.next(); objectMessageList.add(toString(element)); if (element instanceof Class) { if (item.isSetOrigin()) { continue; } final Class c = (Class) element; final String clazz = c.getClassname(); logger.finer("Processing class '" + clazz + "'"); final String javaFile = convertToRelativeJavaFile(clazz); final ResourceInfo info = findResourceInfo(sourceDirs, javaFile); if (info != null) { List itemList = (List) itemMap.get(info); if (itemList == null) { itemList = new ArrayList(); itemMap.put(info, itemList); } item.setOrigin(Origin.FINDBUGS); item.setSeverity(bugInstance.getPriority()); item.setFindingType(bugInstance.getType()); itemList.add(item); logger.finest("Adding findings for resource " + javaFile); } else { logger.finer("Ignoring findings for resource " + javaFile); } } else if (element instanceof SourceLine) { // Can be more specific info so allow override // if given data is not concrete. // There are finders like IL_INFINITE_LOOP which // report additional SourceLine items that point to // informative other lines rather than the buginstance. // Til we know how to get the correct line we should leave it // like that. (see also http://tinyurl.com/ycol9h ff.) if (topLevelSourceLineRead && item.isSetLine() && item.getLine() > 0) { continue; } logger.finer("Adding source line information to item " + item.getFindingType()); final SourceLine sourceLine = (SourceLine) element; if (sourceLine.isSetStart()) { item.setLine(sourceLine.getStart()); topLevelSourceLineRead = true; if (sourceLine.isSetEnd()) { item.setEndLine(sourceLine.getEnd()); } } } else if (element instanceof Method) { if (item.isSetLine()) { continue; } if (((Method) element).isSetSourceLine()) { logger.finer("Adding source line information for method" + " to item " + item.getFindingType()); final SourceLineType sourceLine = ((Method) element).getSourceLine(); if (sourceLine.isSetStart()) { item.setLine(sourceLine.getStart()); if (sourceLine.isSetEnd()) { item.setEndLine(sourceLine.getEnd()); } } } } } } return itemMap; } private ResourceInfo findResourceInfo (List sourceDirs, String javaFile) { ResourceInfo info = null; for (final Iterator iterator = sourceDirs.iterator(); iterator.hasNext();) { final String srcDir = (String) iterator.next() + File.separator; final String key = normalizeFileName(srcDir + javaFile); logger.finest("Looking for file: " + key); info = ResourceInfo.lookup(key); if (info != null) { break; } } return info; } private String toString (Object element) { final String ret; if (element instanceof Class) { final Class c = (Class) element; ret = c.getClassname(); } else if (element instanceof Method) { final Method m = (Method) element; ret = m.getName() + m.getSignature(); } else if (element instanceof Field) { final Field f = (Field) element; ret = f.getName(); } else if (element instanceof SourceLine) { final SourceLine sl = (SourceLine) element; ret = sl.getStart() + "-" + sl.getEnd(); } else if (element instanceof Int) { final Int i = (Int) element; ret = String.valueOf(i.getValue()); } else { ret = String.valueOf(element); } return ret; } private String convertToRelativeJavaFile (String clzznm) { String clazzname = clzznm; if (clazzname.indexOf('$') != -1) // inner clazz { clazzname = clazzname.substring(0, clazzname.indexOf('$')); } return clazzname.replace('.', File.separatorChar) + ".java"; } }