package hudson.plugins.dry.parser.cpd; import hudson.plugins.analysis.util.JavaPackageDetector; import hudson.plugins.analysis.util.model.FileAnnotation; import hudson.plugins.dry.parser.AbstractDryParser; import hudson.plugins.dry.parser.DuplicateCode; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.commons.digester.Digester; import org.xml.sax.SAXException; /** * A parser for PMD's CPD XML files. * * @author Ulli Hafner */ public class CpdParser extends AbstractDryParser { /** Unique ID of this class. */ private static final long serialVersionUID = 6507147028628714706L; /** * Creates a new instance of {@link CpdParser}. * * @param highThreshold * minimum number of duplicate lines for high priority warnings * @param normalThreshold * minimum number of duplicate lines for normal priority warnings */ public CpdParser(final int highThreshold, final int normalThreshold) { super(highThreshold, normalThreshold); } /** {@inheritDoc} */ @Override public boolean accepts(final InputStream file) { try { Digester digester = new Digester(); digester.setValidating(false); digester.setClassLoader(CpdParser.class.getClassLoader()); String duplicationXPath = "*/pmd-cpd"; digester.addObjectCreate(duplicationXPath, String.class); Object result = digester.parse(file); if (result instanceof String) { return true; } } catch (IOException exception) { // ignore and return false } catch (SAXException exception) { // ignore and return false } return false; } /** {@inheritDoc} */ @Override public Collection<FileAnnotation> parse(final InputStream file, final String moduleName) throws InvocationTargetException { try { Digester digester = new Digester(); digester.setValidating(false); digester.setClassLoader(CpdParser.class.getClassLoader()); ArrayList<Duplication> duplications = new ArrayList<Duplication>(); digester.push(duplications); String duplicationXPath = "*/pmd-cpd/duplication"; digester.addObjectCreate(duplicationXPath, Duplication.class); digester.addSetProperties(duplicationXPath); digester.addCallMethod(duplicationXPath + "/codefragment", "setCodeFragment", 0); digester.addSetNext(duplicationXPath, "add"); String fileXPath = duplicationXPath + "/file"; digester.addObjectCreate(fileXPath, SourceFile.class); digester.addSetProperties(fileXPath); digester.addSetNext(fileXPath, "addFile", SourceFile.class.getName()); Object result = digester.parse(file); if (result != duplications) { // NOPMD throw new SAXException("Input stream is not a valid CPD file."); } return convert(duplications, moduleName); } catch (IOException exception) { throw new InvocationTargetException(exception); } catch (SAXException exception) { throw new InvocationTargetException(exception); } } /** * Converts the internal structure to the annotations API. * * @param duplications * the internal maven module * @param moduleName * name of the maven module * @return a maven module of the annotations API */ private Collection<FileAnnotation> convert(final List<Duplication> duplications, final String moduleName) { JavaPackageDetector javaPackageDetector = new JavaPackageDetector(); ArrayList<FileAnnotation> annotations = new ArrayList<FileAnnotation>(); for (Duplication duplication : duplications) { List<DuplicateCode> codeBlocks = new ArrayList<DuplicateCode>(); for (SourceFile file : duplication.getFiles()) { // TODO: check why PMD reports a length + 1 DuplicateCode annotation = new DuplicateCode(getPriority(duplication.getLines()), file.getLine(), duplication.getLines(), file.getPath()); annotation.setSourceCode(duplication.getCodeFragment()); annotation.setModuleName(moduleName); codeBlocks.add(annotation); } for (DuplicateCode block : codeBlocks) { block.linkTo(codeBlocks); String packageName = javaPackageDetector.detectPackageName(block.getFileName()); block.setPackageName(packageName); } annotations.addAll(codeBlocks); } return annotations; } }